「 React.js
漂亮,可移植性 列表拖拽库 」{翻译}
翻译的原文 | 与日期 | 最新更新 | 更多 |
---|---|---|---|
commit | ⏰ 2018 10.31 | 中文翻译 |
- readme
- docs/**
欢迎 👏 勘误/校对/更新贡献 😊 具体贡献请看
更新了版本,但有点走神,多多见谅,大力Issue与PR,致歉
If help, buy me coffee —— 营养跟不上了,给我来瓶营养快线吧! 💰
React.js
美丽,无障碍的列表拖放库
❤️ react-beautiful-dnd 文档目录 ❤️
- 例子🎉
- 基本使用示例
- 核心特点
- 来来来,入门 🤩
- Read this in other languages
- 目前支持的功能集
- 升级
- 并不适合所有人
- 驾驶理念: 物理性
- 精心设计的动画
- 关心交互细节
- 鼠标拖动
- 键盘拖动
- 触摸拖动
- 多拖
- 预设样式
- 安装
ClojureScript
- API
DragDropContext
Droppable
Draggable
resetServerContext
- Flow 使用
- Typescript
- 社区作品
- 其他
- 工程健康
- 大小
- 支持的浏览器
- 作者
- 维护者
- 合作者
看看它对你自己有多美丽!
目前,我们为触摸设备提供不同的故事书{storybook}链接,但没有很好的移动菜单体验更多信息
我们已经创建了一些基本的例子codesandbox
为你直接玩:
即将推出: 获取入门指南!
- 美丽,自然的物品移动
- 简洁且功能强大的api快速开始
- 与标准浏览器交互非常好
- Unopinionated 「没有固执己见」样式
- 没有创建额外的包装DOM节点 - flexbox 和 焦点{focus} 管理友好!
- 无障碍
我们 漂亮地创建了课程 a free course on egghead.io
去帮助人们 尽可能快地入门 react-beautiful-dnd
- 垂直列表 ↕
- 水平列表 ↔
- 列表之间的移动 (▤ ↔ ▤)
- 鼠标 🐭,键盘 🎹 和触摸 👉📱 (手机,平板电脑等) 支持
- 自动滚动 - 在拖动过程中根据需要自动滚动容器和窗口 (即使是使用键盘🔥)
- 多拖动支持
- 令人难以置信的屏幕阅读器支持 - 我们为开箱即用的英文屏幕阅读器提供了惊人的体验. 我们还为需要它的人提供完整的定制控制和国际化支持
- 拖动和放下条件
- 在一个页面上有多个独立的列表
- 灵活的项目大小 - 可拖动的项目可以有不同的高度 (垂直列表) 或宽度 (水平列表)
- 兼容 semantic 表格 重新排序 -表格模式
- 兼容
React.Portal
- Portal 模式「portal pattern」 - 自定义拖动控制 - 您可以通过拖动整个项目的一部分
- 一个
Droppable
列表可以是滚动容器 (没有可滚动的父项) 或者是滚动容器的子项 (也没有可滚动的父项) - 独立的嵌套列表 - 列表可以是另一个列表的子项,但不能将项目从父列表拖到子列表中
- 服务器端渲染兼容
- 默认与嵌套的交互式元素兼容
您可以查看即将登陆的所有功能在我们的问题页面上.
我们在发行说明中创建了升级说明,以帮助您升级到最新版本!
有很多库允许React中的拖放交互. 其中最值得注意的是惊人的react-dnd
. 它提供了一套非常出色的拖放函数,这些函数在特定情况下非常适用疯狂地不一致的html5拖放功能. react-beautiful-dnd
是为垂直和水平列表专门构建的更高级别的抽象. 在该功能的子集内react-beautiful-dnd
提供强大,自然和美丽的拖放体验. 但是,它没有提供 react-dnd 提供的广泛功能. 所以这个库可能不适合你,这取决于你的用例是什么.
核心设计理念react-beautiful-dnd
是物理性的: 我们希望用户感觉他们正在移动物体
这是一个相当标准的拖放模式,用于响应用户拖动而消失并重新出现的东西. 对于更自然的拖动,我们为物品的移动设置动画,因为它们需要在拖动时偏移
以更清晰地显示拖动效果. 我们还制作了一件物品的下落动画,以便将其移动到新的位置. 在任何时候,无论它是否在拖动, 物品都不会随时随地移动.
常见用法: 拖放交互基于用户开始拖动的位置.
在react-beautiful-dnd
拖动物品的影响是基于其重心 - 无论用户从哪里抓取物品. 拖动项目的影响遵循 类似的规则 ⚖️. 以下是一些遵循的规则,即使对于灵活高度的物品也可以实现自然的拖拽体验:
- 当拖动物品的中心位置越过列表的边界之一时, 列表是拖过去
- 当拖动物品的中心位置越过
休息{没拖着}
物品的边缘时,没拖动物品将移出拖动物品的路线. 换句话说: 一旦物品 (A) 的中心位置越过另一物品 (B) 的边缘,B就会移开.
在物品及其目的地四处移动的环境中,阴影很有用. 但是,与react-beautiful-dnd
根据物品的移动情况, 物品的放置位置应该很明显. 这可能会在未来发生变化 - 这一点上是要看看接下来, 我们在没有任何这些可供选择的情况下能够获得多少.
react-beautiful-dnd
真的很难避免尽可能多的非互动性阶段. 用户应该觉得他们在控制界面,而不是等待动画完成,然后才能继续与界面交互. 然而,为了让每个人的生活更加健康,在 正确性和权力 之间需要做出平衡. 以下是有些事情不具有互动性的唯一情况:
- 从用户取消拖动到拖放动画完成时. 取消时有很多事情要回到他们应该在的地方. 如果您在不是 真正的家的
位置
抓取物品,则拖动将不正确. - 真正拖动物品后就开始动画了. 为了简单起见,情况就是这样 - 在实际动画的期间上很难�再抓东西. 它可以被编码 - 但它似乎是一个边缘案例,会增加很多复杂性.
请记住,这些不活动时间可能并不总是存在.
目前,库不支持拖动轴锁定 (又名拖轨) . 这是用户被限制在只沿一个轴拖动的地方. 目前的想法是,这打破了我们正在寻找的物理隐喻,并向用户发送一条消息,即他们正在与一个软件进行交互,而不是移动物理对象. 可以确保用户只能通过使用 Props 放入单个列表中type
和isDropDisabled
. 您也可以对列表进行一些视觉处理到onDragStart
函数,这是唯一的用户显示他们互动的地方.
比起使用基于索引的方法在列表之间移动键盘,react-beautiful-dnd
更倾向于执行交叉表移动 惯性,重力和碰撞. 您可以通过阅读博客了解更多关于这是如何工作的"列表之间的自然键盘移动".
随着事情的发展,用户会很容易被动画分散注意力或阻碍他们前进. 我们调整了各种动画,以确保指导,表演和互动性之间的平衡.
当你拖放一个拖动项目时,它的运动是基于物理的 (谢谢react-motion
) . 这导致放下感觉更加重和物理.
移出拖动项目的项目是通过 CSS过渡 而非物理过程来实现的. 这是通过允许 GPU处理运动来最大化性能. CSS动画曲线的设计是为了避免沟通.
它是如何组成的:
- 一个模拟自然反应时间的热身期
- 一个小阶段,可以快速移除
- 长尾巴,以便人们可以阅读 动画后半部分正在动画时 的任何文字
动画曲线在移动时使用
react-beautiful-dnd
不会创建任何包装元素. 这意味着它不会影响文档的常用选项卡流程. 例如,如果你正在包装一个目标标签,则用户将直接 tab 到目标点,而不是一个围绕该目标的元素. 无论你包装什么元素都会得到一个tab-index
以确保用户通过 tab 拖动.
当用户拖动一个Draggable{可拖动物品}
靠近容器边缘时, 我们会自动滚动容器,以便为其腾出Draggable
空间.
一个容器称为一个
Droppable
,是因为可滚动的或有一个滚动父辈 - 或window
.
鼠标和触摸 | 键盘 |
---|---|
它也适用于多列表所有输入类型的配置
鼠标和触摸 | 键盘 |
---|---|
当一个Draggable
中心在距离容器边缘很小的距离内,我们开始自动滚动. 随着用户靠近容器的边缘,我们增加了自动滚动的速度. 这种加速度使用缓动功能来指数地增加我们移向边缘越近的加速度. 我们从容器的真实边缘到达最大加速度,使得用户不需要非常精确以获得最大的滚动速度. 该逻辑适用于可滚动的任何边缘.
自动滚动所需的距离分别基于容器的垂直和水平滚动的高度或宽度的百分比. 通过使用百分比而不是原始像素值,无论容器的大小和形状如何,我们都可以获得丰富的体验.
除了自动滚动,我们还允许用户滚动窗口或一个Droppable
手动使用他们的鼠标滚轮要么触控板👌
如果Draggable
比您要滚动的轴上的容器大 - 我们将不允许在该轴上滚动. 例如,如果你有一个Draggable
这比窗口的高度更长,我们不会自动垂直滚动. 不过,我们仍然会允许水平滚动.
当在iOS浏览器 (webkit) 上自动滚动时Draggable
明显地震动. 这是由于一个与webkit的错误没有已知的工作. 我们尝试了很长时间来解决这个问题!如果你看到这种改进感兴趣,请与我们联系webkit问题.
拖动键盘时,我们也会根据需要正确更新滚动位置. 为了移动一个Draggable
进入正确的位置,我们可以做一个Droppable
滚动,window
滚动和手动移动的组合以确保Draggable
响应用户移动指令结束在正确的位置. 这是大焦点🔥.
对于有视觉障碍的用户来说,这是惊人的,因为他们可以在大列表中正确移动项目而无需使用鼠标定位.
传统上,拖放交互一直是鼠标或触摸交互. 这个库支持拖放交互 只使用键盘. 这使高级用户可以完全从键盘上获得他们的体验. 以及向以前被排除在外的用户开放这些体验.
我们提供 对屏幕阅读器的精彩支持帮助视觉 (或其他) 损伤的用户. 我们随机附带英文短信📦. 但是,欢迎您使用announce
函数覆盖这些消息, 函数提供给所有的函数DragDropContext > hook
功能.
见我们的屏幕阅读器指南为指导制作有用的屏幕阅读器信息.
当用户在元素上按下鼠标时,我们无法确定用户是在点击还是在拖动. 另外,有时当用户点击时,他们可以稍微移动光标 - 一个马虎的点击. 所以我们只有在用户用鼠标向下移动 超过一定距离 时才会开始拖拽 (拖拽阈值) - 如果他们只是马上点击,它们会比他们更多. 如果没有超过拖拽阈值,则用户交互就像常规点击一样. 如果超过了拖拽阈值,则该交互将被分类为 拖拽 并且不会发生 标准点击行为.
这允许消费者包装诸如目标的交互元素,并且以自然的方式使其既是标准目标也是可拖动项.
(🐱🎁是一个薛定谔的猫玩笑)
要查看更多关于我们如何影响标准浏览器事件的深入信息,请参阅我们的我们如何使用DOM事件指南
当一个拖动 没有发生 react-beautiful-dnd
不会影响任何标准键盘交互 (它没有绑定的监听器) .
当一个拖动 正在发生与鼠标用户可以执行以下键盘快捷键:
- 逃逸 esc- 取消拖动
在鼠标拖动过程中,以下标准键盘事件被阻止,以防止出现不好的体验:
- tab tab ↹- 防止绊倒
- enter ⏎- 防止提交
除了这些明确禁止键盘事件外,所有标准键盘事件都应按预期工作.
react-beautiful-dnd
仅支持使用键盘进行拖动. 我们审核了我们的键盘快捷键如何与标准浏览器键盘交互进行交互. 当用户没有拖动时,他们可以像平常一样使用键盘. 拖动时,我们覆盖并禁用某些浏览器快捷方式 (如tab
) 以确保用户的流畅体验.
要查看更多关于我们如何影响标准浏览器事件的深入信息,请参阅我们的我们如何使用DOM事件指南
当在使用该标准的页面上没有发生拖动时,用户将能够通过 tab tab↹浏览Draggable
可放置元素 前进和 (shift+tab) (shift+) tab↹) 向后移动. 我们通过添加一个tab-index
到了Draggable
. 当一个Draggable
已经触发了 空格键 space, 将 标记一个Draggable
. 这将开始拖动.
一旦开始拖动,可以使用以下键盘快捷键:
- 空格键 space- 放下
Draggable
- 逃逸 esc- 取消拖动
以下命令也可用,但它们依赖于目前的Droppable
的type
:
- 向上箭头 ↑- 移动一个
Draggable
向上Droppable
- 向下箭头 ↓- 移动一个
Draggable
向下在一个Droppable
- 右箭头 →- 移动一个
Draggable
到一个Droppable
到了对目前Droppable
(移至新列表) - 左箭头 ←- 移动一个
Draggable
到一个Droppable
到了剩下目前Droppable
(移至新列表)
- 向上箭头 ↑- 移动一个
Draggable
到一个Droppable
至以上目前Droppable
(移至新列表) - 向下箭头 ↓- 移动一个
Draggable
到一个Droppable
至下面目前Droppable
(移至新列表) - 右箭头 →- 移动一个
Draggable
到了对在目前Droppable
- 左箭头 ←- 移动一个
Draggable
到了剩下在目前Droppable
在拖动过程中,以下标准键盘事件的默认行为被阻止 (通过event.preventDefault()
) 以避免不好的体验:
- tab tab ↹- 防止绊倒
- enter ⏎- 防止提交
react-beautiful-dnd
支持拖动手机和平板电脑等触摸设备.
录制在iPhone 6s上
当用户按下他们的手指 (或其他输入) 时Draggable
我们不确定他们是否有意点击,强按,滚动容器要么拖动. 越多越好react-beautiful-dnd
旨在确保用户默认的交互体验不受影响.
要查看更多关于我们如何影响标准浏览器事件的深入信息,请参阅我们的我们如何使用DOM事件指南
用户可以通过在一个元素上长时间握住手指开始拖动🕑 (长按)
如果用户在定时器完成之前提起手指,然后我们将该事件释放给浏览器,以确定是否执行标准的点击/点击操作. 这可以让你有一个Draggable
这既是可点击的,例如目标点以及可拖动的. 如果该项目被拖动,那么我们会阻止点击操作的发生.
如果我们发现一个touchmove
在长按计时器到期之前,我们取消挂起的拖动并允许用户正常滚动. 这意味着用户需要相当有意和准确地抓住他们. 第一次touchmove
发生时,我们必须选择是否加入本地滚动.
- 如果长按定时器没有过期: 允许本地滚动并防止拖动
- 如果长按定时器具有过期: 拖动已经开始,我们阻止本地滚动
只有 Safari
如果用户在移动元素之前按下元素 (即使拖动已经开始) ,则拖动被取消并且发生标准的力按压操作. 对于一个目标点,这是一个网站预览.
这只是一个想法 - 如果你想要这种行为,你可以添加它.
如果你喜欢,你也可以触发一个振动事件当用户拿起一个Draggable
. 这可以提供用户正在做的事情的触觉反馈. 目前仅在 Android版Chrome 中支持.
class App extends React.Component {
onDragStart = () => {
// good times
if (window.navigator.vibrate) {
window.navigator.vibrate(100);
}
};
/*...*/
}
我们创造了一个多拖动模式你可以建立在顶部react-beautiful-dnd
为了支持拖动多个Draggable
项目一次.
我们应用了许多不可见的样式来促进拖拽体验. 我们使用 style 目标和技术的组合来做到这一点. 库的目标是提供 无选择的造型. 但是,我们确实在默认情况下应用了一些合理的
cursor`拖动控制样式. 这是为了使 库 尽可能简单地工作而设计的. 如果你想使用你自己的鼠标样式,你是不是欢迎. 您所需要做的就是通过使用规则来覆盖 光标 样式规则高定义性.
以下是在拖动生命周期中的各个点上应用的样式:
样式适用于: 拖动控制元素使用data-react-beautiful-dnd-drag-handle
属性.
长按目标点通常会弹出一个内容菜单,其中包含链接选项,例如"在新标签中打开". 由于长按是用来开始拖动,我们需要退出此行为
-webkit-touch-callout: none;
基于 Webkit 的浏览器在目标点处于活动状态时, 为其添加灰色叠加层. 我们删除了这个点按叠加层,因为它会让用户感到困惑. 更多信息.
-webkit-tap-highlight-color: rgba(0,0,0,0);
避免情况拉动以刷新行动和延迟目标定焦点在Android Chrome上
touch-action: manipulation;
样式适用于: droppable元素使用data-react-beautiful-dnd-droppable
属性.
当浏览器功能试图保持滚动位置,当DOM变化超过边缘时,选择退出. 我们已经正确地保持滚动位置. 自动overflow-anchor
行为导致错误的滚动定位后放下.
overflow-anchor: none;
样式适用于: 拖动控制元素使用data-react-beautiful-dnd-drag-handle
属性.
添加一个光标样式让用户知道这个元素是可拖动的. 欢迎您覆盖此.
cursor: grab;
使用该样式应用的样式data-react-beautiful-dnd-drag-handle
属性
拖动时避免处理的优化pointer-events
. 还用于允许滚动通过拖动控制带轨迹板或鼠标滚轮.
pointer-events: none;
使用该样式应用的样式data-react-beautiful-dnd-draggable
属性
这是我们用来控制的Draggable
这需要摆脱拖拉的方式Draggable
.
transition: ${string};
使用内联样式应用的样式
这由类型描述DraggableStyle
.
我们在拖动时应用光标,以向用户反馈发生拖动的情况. 欢迎您覆盖此. 一个好的方面是做到这一点onDragStart
事件.
cursor: grabbing;
为防止用户在拖动时选择文本,应用此样式
user-select: none;
使用该样式应用的样式data-react-beautiful-dnd-drag-handle
属性
我们将 抓取光标应用于所有拖动,除拖动控制Draggable
之外的. 此时用户可以拖动其他Draggable
如果他们喜欢的话.
cursor: grab;
与拖动阶段相同
当用户显式取消拖动时
这与之相同Phase: dropping
. 但我们不适用cursor: grab
拖动控制. 在用户启动取消期间,我们不允许拖放其他项目,直到放下动画完成.
所有应用的样式都是供应商正确的前缀,以符合我们的要求支持浏览器矩阵. 这是手工完成的,以避免通过包含一个 css-in-js 库来增加 react-beautiful-dnd 的大小
# yarn
yarn add react-beautiful-dnd
# npm
npm install react-beautiful-dnd --save
一个通用模块 umd 定义捆绑发布于npm
在下面/dist
文件夹. 我们发布以下文件:
dist/react-beautiful-dnd.js
dist/react-beautiful-dnd.min.js
(缩小束)
这react
捆绑列表作为需要提供的外部. 这样做是为了减少捆绑包的大小并防止消费者加载react
多次. 你可以提供react
通过你的模块系统 或 简单地通过window
拥有react
.
您可以使用 umd 运行react-beautiful-dnd
直接在浏览器中.
<!-- peer dependency -->
<script src="https://unpkg.com/react@16.3.1/umd/react.development.js"></script>
<!-- lib (change x.x.x for the version you would like) -->
<script src="https://unpkg.com/react-beautiful-dnd@x.x.x/dist/react-beautiful-dnd.js"></script>
<!-- needed to mount your react app -->
<script src="https://unpkg.com/react-dom@16.3.1/umd/react-dom.development.js"></script>
<script>
const React = window.React;
const ReactDOM = window.ReactDOM;
const { DragDropContext, Draggable, Droppable } = window.ReactBeautifulDnd;
class App extends React.Component {
//...
}
// You can use JSX if your environment supports it
ReactDOM.render(React.createElement(App), document.getElementById('app'));
</script>
还有一个示例codepen你可以使用这个安装方法来玩.
你可以构建react-beautiful-dnd
使用内部ClojureScript
CLJSJS!
好吧,进入有趣的东西 - 那么你如何使用 库 ?
为了使用拖放,你需要拥有你的部分React
树,你想能够在包裹在一个DragDropContext
使用拖放. 建议将您的整个应用程序包装在一个DragDropContext
. 嵌套DragDropContext
是的不支持的. 你将能够使用 Props 实现你想要的条件拖放Droppable
和Draggable
. 你可以想到DragDropContext
与react-redux 提供程序组件有类似的目的
type Hooks = {|
// optional
onDragBeforeStart?: OnDragBeforeStartHook,
onDragStart?: OnDragStartHook,
onDragUpdate?: OnDragUpdateHook,
// required
onDragEnd: OnDragEndHook,
|};
type OnBeforeDragStartHook = (start: DragStart) => mixed;
type OnDragStartHook = (start: DragStart, provided: HookProvided) => mixed;
type OnDragUpdateHook = (update: DragUpdate, provided: HookProvided) => mixed;
type OnDragEndHook = (result: DropResult, provided: HookProvided) => mixed;
type Props = {|
...Hooks,
children: ?Node,
|};
import { DragDropContext } from 'react-beautiful-dnd';
class App extends React.Component {
onDragStart = () => {
/*...*/
};
onDragUpdate = () => {
/*...*/
}
onDragEnd = () => {
// the only one that is required
};
render() {
return (
<DragDropContext
onDragStart={this.onDragStart}
onDragUpdate={this.onDragUpdate}
onDragEnd={this.onDragEnd}
>
<div>Hello world</div>
</DragDropContext>
);
}
}
Hooks是顶级应用程序事件,您可以使用它来执行自己的状态更新 以及 制作屏幕阅读器公告. 有关控制屏幕阅读器的更多信息,请参阅我们的屏幕阅读器指南
请查看Hooks,指南,了解更多 ❤️
Droppable
组件可以 由一个Draggable
. 他们也 包含 Draggable
们. 一个Draggable
必须包含在一个Droppable
.
import { Droppable } from 'react-beautiful-dnd';
<Droppable droppableId="droppable-1" type="PERSON">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }}
{...provided.droppableProps}
>
<h2>I am a droppable!</h2>
{provided.placeholder}
</div>
)}
</Droppable>;
-
droppableId
: 一个需要DroppableId(string)
唯一标识该应用程序的可投入程序. 请不要更改此 Props - 特别是在拖动时. -
type
: 可选的TypeId(string)
可以用来简单地接受一类Draggable
. 例如,如果您使用该类型PERSON
那么它只会允许Draggable
类型的PERSON
放下自己.Draggable
类型的TASK
将无法被放在Droppable
与类型PERSON
上. 如果不提供type
,它将被设置为'DEFAULT'
. 目前来说Draggable
的type
在Droppable
之内 一定是一样. 如果有一个有效的用例,这个限制在将来可能会放松. -
isDropDisabled
: 可选的标志来控制当前是否允许丢弃Droppable
. 你可以用它来实现你自己的条件丢弃逻辑. 它将默认为false
. -
direction
: 物品在这个物品中流动的方向. 选项是vertical
(默认) 和horizontal
. -
ignoreContainerClipping
: 当一个Droppable
在一个可滚动的容器内,它的区域受到限制,所以你只能放在容器的一部分Droppable
你可以看到. 设置这个 Props 不会出现这种行为,允许你放在任何地方Droppable
即使它被一个可滚动的父级视觉隐藏. 默认行为适用于大多数情况,所以您不需要使用此 Props ,但如果您的 Props 很长,它可能会很有用Draggable
s在一个短的滚动容器内. 请记住,如果您有多个,它可能会导致一些意外的行为Droppable
在同一页面上滚动容器.
该React
一个孩子Droppable
必须是返回a的函数ReactElement
.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => ({
/*...*/
})}
</Droppable>;
该函数提供了两个参数:
type DroppableProvided = {|
innerRef: (?HTMLElement) => void,
droppableProps: DroppableProps,
placeholder: ?ReactElement,
|}
type DroppableProps = {|
// used for shared global styles
'data-react-beautiful-dnd-droppable': string,
|}
provided.innerRef
:为了使 droppable 能够正常工作,你必须 绑定到最高可能的DOM节点中provided.innerRef
.ReactElement
我们这样做是为了避免需要使用查找您的DOM节点.ReactDOM
有关使用的更多信息
看我们的
innerRef
运用指南innerRef
: 这用于在中创建空间
-
provided.placeholder
根据需要拖动.Droppable
当用户拖动不是主列表的列表时,需要此空间. 请确保将 占位符 放在您为其提供 ref 的组件中. 我们需要增加大小本身.Droppable
: 这是一个 Object,它包含需要应用于 Droppable 元素的属性. -
provided.droppableProps (DroppableProps)
它需要应用于您应用的相同元素.provided.innerRef
它目前包含一个data
属性,我们用它来控制一些不可见的CSS.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.droppableProps}>
Good to go
{provided.placeholder}
</div>
)}
</Droppable>;
type DroppableStateSnapshot = {|
// Is the Droppable being dragged over?
isDraggingOver: boolean,
// What is the id of the draggable that is dragging over the Droppable?
draggingOverWith: ?DraggableId,
|};
children
函数还提供有与当前拖动状态有关的少量状态. 这可以选择用于增强组件. 一个常见的用例是正在被拖动的它改变外观.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey' }}
{...provided.droppableProps}
>
I am a droppable!
{provided.placeholder}
</div>
)}
</Droppable>;
-
Droppable
s 只能通过那些共享相同的type
的Droppable
.Draggable
这是允许有条件丢弃的一种简单方法. 如果你不提供一个type
给Droppable
,那么它只会接受默认的类型Draggable
s. 当没有提供Draggable
s 和Droppable
两个人都会有他们的types
设置'DEFAULT'
. 目前没有办法设置多个types
或者一个type
通配符,将接受Draggable
s 的多种任何类型. 如果有一个有效的用例,可以添加它. -
使用
isDropDisabled
你可以有条件地允许掉落. 这使您可以执行任意复杂的条件转换. 这只会被视为如果Droppable
的type
, 匹配type
当前正在拖动Draggable
. -
您可以禁用放弃
Droppable
总是由总设定isDropDisabled
到true
. 你可以这样做来创建一个永远不能被拖放但包含的列表Draggable
s. -
技术上你不需要使用
type
并用你的所有条件放置逻辑来完成isDropDisabled
函数. 该type
参数是常见用例的便捷捷径.
该库支持在滚动容器 (具有DOM元素) 中拖动overflow: auto;
要么overflow: scroll;
) . 该 只要支持的用例有:
- 该
Droppable
本身可以是一个滚动容器 没有可滚动的父母 - 该
Droppable
具有 一个可滚动的父项
哪里一个可滚动的父级指的是一个不是窗口本身的滚动容器.
建议你把一个min-height
给到在垂直Droppable
或者一个min-width
给到在水平Droppable
. 否则当Droppable
是空的可能没有足够的目标Draggable
被触摸或鼠标输入过度拖动该Droppable
.
当用户拖拉或停止拖动一个Droppable
时,我们重新渲染Droppable
与更新DroppableStateSnapshot > isDraggingOver
值. 这对于设计样式非常有用Droppable
. 但是,默认情况下,这将导致所有的孩子的渲染Droppable
- 可能是100个Draggable
的!这可能导致明显的帧速放下. 为了避免这个问题,我们建议您创建一个子组件Droppable
组件,责任是避免渲染 children ,如果不是必需的话.
以下是你如何做到这一点的例子:
import React, { Component } from 'react';
class Student extends Component<{ student: Person }> {
render() {
// Renders out a draggable student
}
}
class InnerList extends Component<{ students: Person[] }> {
// do not re-render if the students list has not changed
shouldComponentUpdate(nextProps: Props) {
if(this.props.students === nextProps.students) {
return false;
}
return true;
}
// You could also not do your own shouldComponentUpdate check and just
// extend from React.PureComponent
render() {
return this.props.students.map((student: Person) => (
<Student student={student} />
))
}
}
class Students extends Component {
render() {
return (
<Droppable droppableId="list">
{(provided: DroppableProvided, snapshot: DroppableStateSnapshot) => (
<div
ref={provided.innerRef}
style={{ backgroundColor: provided.isDragging ? 'green' : 'lightblue' }}
{...provided.droppableProps}
>
<InnerList students={this.props.students} />
{provided.placeholder}
</div>
)}
</Droppable>
)
}
}
通过使用该方法,您可以将样式更改为一个Droppable
当它被拖动时,但你避免不必要地重新渲染所有的孩子. 请记住,如果你正在使用React.PureComponent
你的组件会对上下文中的变化没有反应.
不幸的是我们无法为您应用此优化. 它是使用函数作为子模式的副产品.
Draggable
组件可以拖动并拖放到其Droppable
s上. 一个Draggable
必须始终包含在一个Droppable
. 它是 可能重新排序Draggable
在其Droppable
家中或移动到另一个Droppable
. 它是 可能因为一个Droppable
可以自由地控制它允许投入的内容.
每个Draggable
有一个拖动控制. 一个拖动控制是用户为了拖动而与之交互的元素Draggable
. 一个拖动控制可以是Draggable
元素本身,还是孩子的Draggable
.
import { Draggable } from 'react-beautiful-dnd';
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<h4>My draggable</h4>
</div>
)}
</Draggable>;
注意: 当库移动到React 16时,这将被清理一些,因为我们可以将占位符作为兄弟返回到您的子函数,而无需创建包装元素
draggableId
: 一个需要DraggableId(string)
唯一标识的Draggable
为应用程序. 请不要更改此 Props - 特别是在拖动时.index
: 一个需要number
它与Draggable
的顺序相匹配在Droppable
里面. 它只是简单的索引Draggable
在列表中. 该index
在一个内部需要是唯一的Droppable
, 但不需要是唯一的Droppables
. 通常情况下index
价值将是简单的index
由Array.prototype.map
函数提供:
{
this.props.items.map((item, index) => (
<Draggable draggableId={item.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
{item.content}
</div>
)}
</Draggable>
));
}
isDragDisabled
: 可选的标志来控制是否Draggable
被允许拖动. 您可以使用它来实现自己的条件拖动逻辑. 它将默认为false
.disableInteractiveElementBlocking
: 可选的标志选择不阻止交互式元素的拖动. 有关更多信息,请参阅本节交互式的子元素Draggable
该React
一个孩子Draggable
必须是返回一个函数ReactElement
.
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
Drag me!
</div>
)}
</Draggable>;
该函数提供了两个参数:
type DraggableProvided = {|
innerRef: (HTMLElement) => void,
draggableProps: DraggableProps,
// will be null if the draggable is disabled
dragHandleProps: ?DragHandleProps,
|}
提供的对象中的所有内容必须适用于“Draggable”才能正常运行.
provided.innerRef (innerRef: (HTMLElement) => void)
: 为了Droppable
正确运行, 你必须 绑定innerRef
函数到你认为的ReactElement
节点.Draggable
我们这样做是为了避免需要使用查找您的DOM节点.ReactDOM
有关使用的更多信息
看我们的
innerRef
运用指南innerRef
例
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => <div ref={provided.innerRef}>Drag me!</div>}
</Draggable>;
provided.draggableProps (DraggableProps)
这是一个包含的data
属性和内联style
的 Object , 此对象需要应用于您应用的同一节点至provided.innerRef
. 这将控制可拖动或不可拖动的操作. 欢迎您将自己的样式添加到DraggableProps.style
- 但请不要移除或替换任何属性.
// Props that can be spread onto the element directly
export type DraggableProps = {|
// inline style
style: ?DraggableStyle,
// used for shared global styles
'data-react-beautiful-dnd-draggable': string,
|}
type DraggableStyle = DraggingStyle | NotDraggingStyle
type DraggingStyle = {|
position: 'fixed',
width: number,
height: number,
boxSizing: 'border-box',
pointerEvents: 'none',
top: number,
left: number,
margin: 0,
transition: 'none',
transform: ?string,
zIndex: ZIndex,
|}
type NotDraggingStyle = {|
transform: ?string,
transition: null | 'none',
|}
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.draggableProps}>
Drag me!
</div>
)}
</Draggable>;
它是该库的一个合约,它拥有拖动元素的定位逻辑.
这包括诸如top
,right
,bottom
,left
和transform
. 库 可能会改变它是如何定位事物以及它使用的是哪些属性,而不会执行主要的版本冲突. 也建议你不要使用你自己的transition
属性添加到拖动元素.
react-beautiful-dnd
使用position: fixed
定位拖动元素. 这是相当强大的,并允许你有position: relative | absolute | fixed
父母. 然而,不幸的是position:fixed
是受到影响transform
(如transform: rotate(10deg);
) . 这意味着如果你有一个transform: *
在Draggable
的父母之一时, 那么拖动时定位逻辑将不正确. 瘸!对于大多数消费者来说,这不会是一个问题.
为了解决这个问题,你可以使用React.Portal
. 我们不会默认启用此功能,因为它有性能问题. 我们有一个使用 Portal 指南更详细地解释性能问题以及如何设置自己的问题React.Portal
如果你想.
当移动一个Draggable
从一个列表到另一个列表,默认的浏览器行为是为了拖动控制元素放松焦点. 这是因为旧元素正在被破坏,并且正在创建一个新元素. 当用键盘拖动时,焦点丢失并不好,因为用户无法继续与元素交互. 为了改善这种用户体验,我们自动给出一个拖动控制焦点 当:
- 它在拖动结束时被卸载
- 它有焦点
- 它在安装时启用
- 在拖动控制安装之前,没有其他元素获得浏览器焦点
如果您使用内联样式,欢迎您将其扩展DraggableProps.style
目的. 也欢迎申请DraggableProps.style
使用内联样式的对象,并为组件本身使用自己的样式解决方案 - 例如风格组件.
如果您重写内联样式,请务必在进行provided.draggableProps
或者传播展开后会覆盖你的内联风格.
<Draggable draggable="draggable-1" index={0}>
{(provided, snapshot) => {
// extending the DraggableStyle with our own inline styles
const style = {
backgroundColor: snapshot.isDragging ? 'blue' : 'white',
fontSize: 18,
...provided.draggableProps.style,
};
return (
<div
ref={provided.innerRef}
{...provided.draggableProps}
style={style}
>
Drag me!
</div>
);
}}
</Draggable>;
边缘崩溃是CSS中非常困难的部分之一. 对于我们的目的,如果你有一个Draggable
与margin-bottom: 10px
和下一个Draggable
有一个margin-top: 12px
这些折叠将会坍方并由此产生的余量将是两者中较大的一个: 12px
. 当我们进行我们的计算时,我们目前没有考虑边缘折叠. 如果你确实想在兄弟姐妹身上留下一个空白,那么把它们都包在一个div
并将边缘应用于内部div
, 所以他们不是直系兄弟姐妹.
这是一个假设Draggable
s是可见的兄弟姐妹彼此之间. 中间还可以有其他元素,但这些元素不应占用额外的空间. 无论如何,你可能不会这样做,但只要说出来就非常清楚.
// Direct siblings ✅
<Draggable draggableId="draggable-1" index={0}>
{() => {}}
</Draggable>
<Draggable draggableId="draggable-2" index={1}>
{() => {}}
</Draggable>
// Not direct siblings, but are visible siblings ✅
<div>
<Draggable draggableId="draggable-1" index={0}>
{() => {}}
</Draggable>
</div>
<div>
<Draggable draggableId="draggable-2" index={1}>
{() => {}}
</Draggable>
</div>
// Spacer elements ❌
<Draggable draggableId="draggable-1" index={0}>
{() => {}}
</Draggable>
<p>I will break things!</p>
<Draggable draggableId="draggable-2" index={1}>
{() => {}}
</Draggable>
// Spacing on non sibling wrappers ❌
<div style={{padding: 10}}>
<Draggable draggableId="draggable-1" index={0}>
{() => {}}
</Draggable>
</div>
<div style={{padding: 10}}>
<Draggable draggableId="draggable-2" index={1}>
{() => {}}
</Draggable>
</div>
provided.dragHandleProps (?DragHandleProps)
一切Draggable
有一个拖动控制. 这是用来拖动整个的Draggable
. 通常这将是对Draggable
,但有时它可能是一个Draggable
的孩子.DragHandleProps
需要应用到您想要成为拖动控制的节点. 这是一些 Props 需要应用到Draggable
节点. 最简单的方法是将 Props 散布到 可拖动节点上 ({...provided.dragHandleProps}
) . 但是,你也欢迎猴子补丁这些 Props 如果你还需要回应他们. DragHandleProps将会null
当isDragDisabled
被设定为true
.
type DragHandleProps = {|
onFocus: () => void,
onBlur: () => void,
onMouseDown: (event: MouseEvent) => void,
onKeyDown: (event: KeyboardEvent) => void,
onTouchStart: (event: TouchEvent) => void,
'data-react-beautiful-dnd-drag-handle': string,
'aria-roledescription': string,
tabIndex: number,
draggable: boolean,
onDragStart: (event: DragEvent) => void,
|}
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
Drag me!
</div>
)}
</Draggable>;
通过它的一部分控制整个可拖动
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => (
<div ref={provided.innerRef} {...provided.draggableProps}>
<h2>Hello there</h2>
<div {...provided.dragHandleProps}>Drag handle</div>
</div>
)}
</Draggable>;
你可以重写一些dragHandleProps
, 如果你需要的话,你可以使用自己的行为.
const myOnMouseDown = event => console.log('mouse down on', event.target);
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => {
const onMouseDown = (() => {
// dragHandleProps might be null
if (!provided.dragHandleProps) {
return onMouseDown;
}
// creating a new onMouseDown function that calls myOnMouseDown as well as the drag handle one.
return (event) => {
provided.dragHandleProps.onMouseDown(event);
myOnMouseDown(event);
};
})();
return (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
onMouseDown={onMouseDown}
>
Drag me!
</div>
);
}}
</Draggable>;
type DraggableStateSnapshot = {|
// 如果正在拖动Draggable,或者如果它是drop动画,则设置为true
// 主动拖动和拖放动画都被视为拖动的一部分
isDragging: boolean,
// 如果Draggable是放置动画,则设置为true。 不是每次拖放互动
// 作为放置动画。 当Draggable已经进入最终掉落时的位置时,没有放置动画
// 使用键盘拖动时通常会出现这种情况
isDropAnimating: boolean,
// 拖动 Draggable 在确切覆盖 (如果有的话)着的Droppable id
draggingOver: ?DroppableId,
|};
该children
函数还提供有与当前拖动状态有关的少量状态.这可以选择用于增强组件. 一个常见的用例是改变Draggable
的外观当它被拖拽. 注意: 如果你想改变光标到类似grab
的东西您需要将样式添加到可拖动. (看到扩展DraggableProps.style
以上)
<Draggable draggableId="draggable-1" index={0}>
{(provided, snapshot) => {
const style = {
backgroundColor: snapshot.isDragging ? 'blue' : 'grey',
...provided.draggableProps.style,
};
return (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
style={style}
>
Drag me!
</div>
);
}}
</Draggable>;
当拖动一个Draggable
我们留下了一个占位符 React.Element
保持空间Droppable
以防止它崩溃. 占位符模仿样式和布局 (包括width
,height
,margin
,tagName
和display
) 以确保列表尺寸在拖动时不受影响. 它将被react-beautiful-dnd
作为直接兄弟插入由Draggable
子函数返回的React.Node
。
欢迎您添加您自己的onClick
处理程序Draggable
或者一个拖动控制 (这可能是相同的元素) . onClick
如果发生点击,事件处理程序将始终被调用. 如果我们阻止点击,那么我们就是event.defaultPrevented
财产将被设置为true
. 我们阻止发生点击事, 当用户拖动项目时 件. 看到#马虎点击并点击预防🐱🎁 了解更多信息.
这是你的Draggable
可能包含交互元素. 默认情况下,我们阻止拖动这些元素. 通过这样做,我们允许这些元素以通常的方式运行. 以下是默认阻止拖动的交互式元素列表:
input
button
textarea
select
option
optgroup
video
audio
contenteditable
(任何元素是contenteditable
或者在一个contenteditable
容器)
您可以通过添加选项disableInteractiveElementBlocking
到Draggable
来退出此行为. 但是,您是否应该这样做是值得怀疑的,因为它会使交互式元素无法使用. 如果你需要有条件的阻止交互式元素拖动,可添加disableInteractiveElementBlocking
退出默认的阻塞 和 猴子补丁到dragHandleProps (DragHandleProps)
事件处理程序禁用拖动.
该resetServerContext
函数应该在服务器端呈现 (SSR) 时使用. 它确保 上下文状态 不会在服务器上的多个呈现中持续存在,这会在服务器上呈现多个请求后 导致 客户端/服务器标记 不匹配.
在调用服务器端渲染方法之前使用它:
import { resetServerContext } from 'react-beautiful-dnd';
import { renderToString } from 'react-dom/server';
...
resetServerContext();
renderToString(...);
react-beautiful-dnd
是使用 类型化 的flowtype
. 这大大提高了代码库内部的一致性. 我们还公开了许多公共类型,如果您愿意,可以输入您的 javascript 类型. 如果你不使用flowtype
这不会阻止你使用该库. 对于那些想要它的人来说,这只是额外的安全.
// id's
type Id = string;
type TypeId = Id;
type DroppableId = Id;
type DraggableId = Id;
// hooks
type DragStart = {|
draggableId: DraggableId,
type: TypeId,
source: DraggableLocation,
|}
type DragUpdate = {|
...DragStart,
// may not have any destination (drag to nowhere)
destination: ?DraggableLocation,
|}
type DropResult = {|
...DragUpdate,
reason: DropReason,
|}
type DropReason = 'DROP' | 'CANCEL'
type DraggableLocation = {|
droppableId: DroppableId,
// the position of the droppable within a droppable
index: number
|};
// Droppable
type DroppableProvided = {|
innerRef: (?HTMLElement) => void,
placeholder: ?ReactElement,
|}
type DroppableStateSnapshot = {|
isDraggingOver: boolean,
draggingOverWith: ?DraggableId,
|}
// Draggable
type DraggableProvided = {|
innerRef: (?HTMLElement) => void,
draggableProps: DraggableProps,
dragHandleProps: ?DragHandleProps,
|}
type DraggableStateSnapshot = {|
isDragging: boolean,
isDropAnimating: boolean,
draggingOver: ?DroppableId,
|}
export type DraggableProps = {|
style: ?DraggableStyle,
'data-react-beautiful-dnd-draggable': string,
|}
type DraggableStyle = DraggingStyle | NotDraggingStyle
type DraggingStyle = {|
position: 'fixed',
width: number,
height: number,
boxSizing: 'border-box',
pointerEvents: 'none',
top: number,
left: number,
transition: 'none',
transform: ?string,
zIndex: ZIndex,
|}
type NotDraggingStyle = {|
transition: ?string,
transition: null | 'none',
|}
type DragHandleProps = {|
onFocus: () => void,
onBlur: () => void,
onMouseDown: (event: MouseEvent) => void,
onKeyDown: (event: KeyboardEvent) => void,
onTouchStart: (event: TouchEvent) => void,
'data-react-beautiful-dnd-drag-handle': string,
'aria-roledescription': string,
tabIndex: number,
draggable: boolean,
onDragStart: (event: DragEvent) => void,
|}
这些类型作为模块的一部分导出,因此使用它们非常简单:
import type { DroppableProvided } from 'react-beautiful-dnd';
如果你正在使用Typescript你可以使用社区维护DefinitelyTyped 类型定义. 安装说明.
这有些 typescript 的例子.
我们创造了一个简单示例它练习 flow . 这是一个超级简单React
基于react-create-app
. 你可以用它作为参考,看看如何正确设置.
- kanban-dnd - 一个看云样式的TODO列表,能够创建自定义通道,并在运行中重新排序.
- Simple Trello - Trello的简单clone版本, 使用 React 生态.
- natural-drag-animation-rbdnd adds natural dragging animation
这个代码库是用flowtype的类型化促进更大的内部一致性和更有弹性的代码.
该代码库使用许多不同的测试策略,包括单元,性能和集成测试. 测试系统的各个方面有助于提高其质量和稳定性.
代码覆盖率不是代码健康的保证,这是一个很好的指标. 此代码库目前位于 ~90%的覆盖率 .
这个代码库被设计为 非常高效- 它是它的DNA的一部分. 它旨在执行尽可能少的更新. 您可以阅读关于完成的性能工作react-beautiful-dnd
这里:
已经非常谨慎地保持 库 尽可能轻. 这是目前 ~38kb(gzip) 在尺寸方面. 如果您已经在使用某个基础依赖关系,那么净成本可能会更低.
该库支持该标准Atlassian 支持的浏览器用于桌面:
桌面 | 版 |
---|---|
Microsoft Internet Explorer (Windows) | 版本11 (Need to [polyfill Array.prototype.find ][iepolyfill]) |
Microsoft Edge | 支持最新的稳定版本 |
Mozilla Firefox (所有平台) | 支持最新的稳定版本 |
Google Chrome (Windows和Mac) | 支持最新的稳定版本 |
Safari (Mac) | 支持最新OS版本的最新稳定版本 |
移动 | 版 |
---|---|
Chrome (Android和iOS) | 支持最新的稳定版本 |
移动Safari (iOS) | 支持最新的稳定版本 |
Android (Android) | Android 4.0.3上的默认浏览器 (冰淇淋三明治-Ice Cream Sandwich) |
[iepolyfill]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find#Polyfill))
亚历克斯里尔登 -@alexandereardon
杰瑞德克罗 -@jaredjcrowe
Bogdan Chadkin -@IAmTrySound
卢克 Batchelor -@alukebatchelor
很多 别的@Atlassian的!