Home

Awesome

react-beautiful-dnd explain translate-svg

React.js 漂亮,可移植性 列表拖拽库 」{翻译}


更新 ✅

<!-- doc-templite START generated --> <!-- time = '2018 10.31' --> <!-- repo = 'atlassian/react-beautiful-dnd' --> <!-- commit = '5fc46a8296e8a91ab7e417d0a948c2f69cfafd43' -->
翻译的原文与日期最新更新更多
commit⏰ 2018 10.31last中文翻译
<!-- doc-templite END generated -->

贡献

欢迎 👏 勘误/校对/更新贡献 😊 具体贡献请看

更新了版本,但有点走神,多多见谅,大力Issue与PR,致歉

生活

If help, buy me coffee —— 营养跟不上了,给我来瓶营养快线吧! 💰


react beautiful dnd

react-beautiful-dnd

React.js 美丽,无障碍的列表拖放库

CircleCI branch npm dependencies Greenkeeper badge SemVer

quote application example

<details> <summary> <strong> ❤️ react-beautiful-dnd 文档目录 ❤️</strong> </summary> <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> <!-- END doctoc generated TOC please keep comment here to allow auto update --> </details>

例子🎉

看看它对你自己有多美丽!

在桌面上查看

所有的例子!

在手机或平板电脑上查看

目前,我们为触摸设备提供不同的故事书{storybook}链接,但没有很好的移动菜单体验更多信息

基本使用示例

我们已经创建了一些基本的例子codesandbox为你直接玩:

即将推出: 获取入门指南!

核心特点

来来来,入门 🤩

我们 漂亮地创建了课程 a free course on egghead.io 去帮助人们 尽可能快地入门 react-beautiful-dnd

Course logo

Read this in other languages

目前支持的功能集

更多即将推出

您可以查看即将登陆的所有功能在我们的问题页面上.

升级

我们在发行说明中创建了升级说明,以帮助您升级到最新版本!

并不适合所有人

有很多库允许React中的拖放交互. 其中最值得注意的是惊人的react-dnd. 它提供了一套非常出色的拖放函数,这些函数在特定情况下非常适用疯狂地不一致的html5拖放功能. react-beautiful-dnd 是为垂直和水平列表专门构建的更高级别的抽象. 在该功能的子集内react-beautiful-dnd提供强大,自然和美丽的拖放体验. 但是,它没有提供 react-dnd 提供的广泛功能. 所以这个库可能不适合你,这取决于你的用例是什么.

驾驶理念: 物理性

核心设计理念react-beautiful-dnd是物理性的: 我们希望用户感觉他们正在移动物体

应用1: 没有即时移动

这是一个相当标准的拖放模式,用于响应用户拖动而消失并重新出现的东西. 对于更自然的拖动,我们为物品的移动设置动画,因为它们需要在拖动时偏移以更清晰地显示拖动效果. 我们还制作了一件物品的下落动画,以便将其移动到新的位置. 在任何时候,无论它是否在拖动, 物品都不会随时随地移动.

应用2: 知道何时移动

常见用法: 拖放交互基于用户开始拖动的位置.

react-beautiful-dnd拖动物品的影响是基于其重心 - 无论用户从哪里抓取物品. 拖动项目的影响遵循 类似的规则 ⚖️. 以下是一些遵循的规则,即使对于灵活高度的物品也可以实现自然的拖拽体验:

应用3: 没有阴影

在物品及其目的地四处移动的环境中,阴影很有用. 但是,与react-beautiful-dnd根据物品的移动情况, 物品的放置位置应该很明显. 这可能会在未来发生变化 - 这一点上是要看看接下来, 我们在没有任何这些可供选择的情况下能够获得多少.

应用4: 最大化交互性

react-beautiful-dnd真的很难避免尽可能多的非互动性阶段. 用户应该觉得他们在控制界面,而不是等待动画完成,然后才能继续与界面交互. 然而,为了让每个人的生活更加健康,在 正确性和权力 之间需要做出平衡. 以下是有些事情不具有互动性的唯一情况:

  1. 从用户取消拖动到拖放动画完成时. 取消时有很多事情要回到他们应该在的地方. 如果您在不是 真正的家的位置 抓取物品,则拖动将不正确.
  2. 真正拖动物品后就开始动画了. 为了简单起见,情况就是这样 - 在实际动画的期间上很难再抓东西. 它可以被编码 - 但它似乎是一个边缘案例,会增加很多复杂性.

请记住,这些不活动时间可能并不总是存在.

应用5: 无拖动轴锁定

目前,库不支持拖动轴锁定 (又名拖轨) . 这是用户被限制在只沿一个轴拖动的地方. 目前的想法是,这打破了我们正在寻找的物理隐喻,并向用户发送一条消息,即他们正在与一个软件进行交互,而不是移动物理对象. 可以确保用户只能通过使用 Props 放入单个列表中typeisDropDisabled. 您也可以对列表进行一些视觉处理到onDragStart函数,这是唯一的用户显示他们互动的地方.

应用6: 自然交叉表移动

比起使用基于索引的方法在列表之间移动键盘,react-beautiful-dnd更倾向于执行交叉表移动 惯性,重力和碰撞. 您可以通过阅读博客了解更多关于这是如何工作的"列表之间的自然键盘移动".

例子🌰

精心设计的动画

随着事情的发展,用户会很容易被动画分散注意力或阻碍他们前进. 我们调整了各种动画,以确保指导,表演和互动性之间的平衡.

删除

当你拖放一个拖动项目时,它的运动是基于物理的 (谢谢react-motion) . 这导致放下感觉更加重和物理.

走出困境

移出拖动项目的项目是通过 CSS过渡 而非物理过程来实现的. 这是通过允许 GPU处理运动来最大化性能. CSS动画曲线的设计是为了避免沟通.

它是如何组成的:

  1. 一个模拟自然反应时间的热身期
  2. 一个小阶段,可以快速移除
  3. 长尾巴,以便人们可以阅读 动画后半部分正在动画时 的任何文字

animation curve

动画曲线在移动时使用

关心交互细节

焦点管理

react-beautiful-dnd不会创建任何包装元素. 这意味着它不会影响文档的常用选项卡流程. 例如,如果你正在包装一个目标标签,则用户将直接 tab 到目标点,而不是一个围绕该目标的元素. 无论你包装什么元素都会得到一个tab-index以确保用户通过 tab 拖动.

自动滚动

当用户拖动一个Draggable{可拖动物品}靠近容器边缘时, 我们会自动滚动容器,以便为其腾出Draggable空间.

一个容器称为一个Droppable,是因为可滚动的或有一个滚动父辈 - 或window.

鼠标和触摸键盘
auto-scroll-mouseauto-scroll-keyboard

它也适用于多列表所有输入类型的配置

鼠标和触摸键盘
auto-scroll-board-mouseauto-scroll-board-keyboard

对于鼠标和触摸输入 🐭📱

当一个Draggable中心在距离容器边缘很小的距离内,我们开始自动滚动. 随着用户靠近容器的边缘,我们增加了自动滚动的速度. 这种加速度使用缓动功能来指数地增加我们移向边缘越近的加速度. 我们从容器的真实边缘到达最大加速度,使得用户不需要非常精确以获得最大的滚动速度. 该逻辑适用于可滚动的任何边缘.

自动滚动所需的距离分别基于容器的垂直和水平滚动的高度或宽度的百分比. 通过使用百分比而不是原始像素值,无论容器的大小和形状如何,我们都可以获得丰富的体验.

鼠标滚轮和触控板

除了自动滚动,我们还允许用户滚动窗口或一个Droppable手动使用他们的鼠标滚轮要么触控板👌

关于大Draggable的笔记

如果Draggable比您要滚动的轴上的容器大 - 我们将不允许在该轴上滚动. 例如,如果你有一个Draggable这比窗口的高度更长,我们不会自动垂直滚动. 不过,我们仍然会允许水平滚动.

iOS自动滚动摇晃 📱🤕

当在iOS浏览器 (webkit) 上自动滚动时Draggable明显地震动. 这是由于一个与webkit的错误没有已知的工作. 我们尝试了很长时间来解决这个问题!如果你看到这种改进感兴趣,请与我们联系webkit问题.

用于键盘拖动 🎹

拖动键盘时,我们也会根据需要正确更新滚动位置. 为了移动一个Draggable进入正确的位置,我们可以做一个Droppable滚动,window滚动和手动移动的组合以确保Draggable响应用户移动指令结束在正确的位置. 这是大焦点🔥.

对于有视觉障碍的用户来说,这是惊人的,因为他们可以在大列表中正确移动项目而无需使用鼠标定位.

无障碍

传统上,拖放交互一直是鼠标或触摸交互. 这个库支持拖放交互 只使用键盘. 这使高级用户可以完全从键盘上获得他们的体验. 以及向以前被排除在外的用户开放这些体验.

我们提供 对屏幕阅读器的精彩支持帮助视觉 (或其他) 损伤的用户. 我们随机附带英文短信📦. 但是,欢迎您使用announce函数覆盖这些消息, 函数提供给所有的函数DragDropContext > hook功能.

见我们的屏幕阅读器指南为指导制作有用的屏幕阅读器信息.

示例屏幕阅读器行为

screen-reader-text

鼠标拖动

马虎点击并点击预防🐱🎁

当用户在元素上按下鼠标时,我们无法确定用户是在点击还是在拖动. 另外,有时当用户点击时,他们可以稍微移动光标 - 一个马虎的点击. 所以我们只有在用户用鼠标向下移动 超过一定距离 时才会开始拖拽 (拖拽阈值) - 如果他们只是马上点击,它们会比他们更多. 如果没有超过拖拽阈值,则用户交互就像常规点击一样. 如果超过了拖拽阈值,则该交互将被分类为 拖拽 并且不会发生 标准点击行为.

这允许消费者包装诸如目标的交互元素,并且以自然的方式使其既是标准目标也是可拖动项.

(🐱🎁是一个薛定谔的猫玩笑)

要查看更多关于我们如何影响标准浏览器事件的深入信息,请参阅我们的我们如何使用DOM事件指南

键盘快捷键: 鼠标拖动

当一个拖动 没有发生 react-beautiful-dnd不会影响任何标准键盘交互 (它没有绑定的监听器) .

当一个拖动 正在发生鼠标用户可以执行以下键盘快捷键:

在鼠标拖动过程中,以下标准键盘事件被阻止,以防止出现不好的体验:

除了这些明确禁止键盘事件外,所有标准键盘事件都应按预期工作.

键盘拖动

react-beautiful-dnd仅支持使用键盘进行拖动. 我们审核了我们的键盘快捷键如何与标准浏览器键盘交互进行交互. 当用户没有拖动时,他们可以像平常一样使用键盘. 拖动时,我们覆盖并禁用某些浏览器快捷方式 (如tab) 以确保用户的流畅体验.

要查看更多关于我们如何影响标准浏览器事件的深入信息,请参阅我们的我们如何使用DOM事件指南

键盘快捷键: 键盘拖动

当在使用该标准的页面上没有发生拖动时,用户将能够通过 tab <kbd>tab↹</kbd>浏览Draggable可放置元素 前进和 (shift+tab) (<kbd>shift</kbd>+) <kbd>tab↹</kbd>) 向后移动. 我们通过添加一个tab-index到了Draggable. 当一个Draggable已经触发了 空格键 <kbd>space</kbd>, 将 标记一个Draggable. 这将开始拖动.

一旦开始拖动,可以使用以下键盘快捷键:

以下命令也可用,但它们依赖于目前Droppabletype:

在一个垂直列表中

在一个水平列表中

在拖动过程中,以下标准键盘事件的默认行为被阻止 (通过event.preventDefault()) 以避免不好的体验:

触摸拖动

react-beautiful-dnd支持拖动手机和平板电脑等触摸设备.

Mobile landscape

录制在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项目一次.

multi drag demo

预设样式

我们应用了许多不可见的样式来促进拖拽体验. 我们使用 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.

(阶段: 拖动) : body元素

我们在拖动时应用光标,以向用户反馈发生拖动的情况. 欢迎您覆盖此. 一个好的方面是做到这一点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文件夹. 我们发布以下文件:

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你可以使用这个安装方法来玩.

ClojureScript

你可以构建react-beautiful-dnd使用内部ClojureScriptCLJSJS!

API

好吧,进入有趣的东西 - 那么你如何使用 库 ?

DragDropContext

为了使用拖放,你需要拥有你的部分React树,你想能够在包裹在一个DragDropContext使用拖放. 建议将您的整个应用程序包装在一个DragDropContext. 嵌套DragDropContext是的支持的. 你将能够使用 Props 实现你想要的条件拖放DroppableDraggable. 你可以想到DragDropContextreact-redux 提供程序组件有类似的目的

Props

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

钩子们{Hook}

Hooks是顶级应用程序事件,您可以使用它来执行自己的状态更新 以及 制作屏幕阅读器公告. 有关控制屏幕阅读器的更多信息,请参阅我们的屏幕阅读器指南

请查看Hooks,指南,了解更多 ❤️

Droppable

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

可拖 Props

子函数

React一个孩子Droppable必须是返回a的函数ReactElement.

<Droppable droppableId="droppable-1">
  {(provided, snapshot) => ({
    /*...*/
  })}
</Droppable>;

该函数提供了两个参数:

1.提供: (DroppableProvided) **:

type DroppableProvided = {|
  innerRef: (?HTMLElement) => void,
  droppableProps: DroppableProps,
  placeholder: ?ReactElement,
|}

type DroppableProps = {|
  // used for shared global styles
  'data-react-beautiful-dnd-droppable': string,
|}

看我们的innerRef运用指南innerRef: 这用于在中创建空间

<Droppable droppableId="droppable-1">
  {(provided, snapshot) => (
    <div ref={provided.innerRef} {...provided.droppableProps}>
      Good to go

      {provided.placeholder}
    </div>
  )}
</Droppable>;

2.快照: (DroppableStateSnapshot)

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

有条件地放下

滚动容器

该库支持在滚动容器 (具有DOM元素) 中拖动overflow: auto;要么overflow: scroll;) . 该 只要支持的用例有:

  1. Droppable本身可以是一个滚动容器 没有可滚动的父母
  2. Droppable具有 一个可滚动的父项

哪里一个可滚动的父级指的是一个不是窗口本身的滚动容器.

Droppable

建议你把一个min-height给到在垂直Droppable或者一个min-width给到在水平Droppable. 否则当Droppable是空的可能没有足够的目标Draggable被触摸或鼠标输入过度拖动该Droppable.

推荐 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

Draggable组件可以拖动并拖放到其Droppables上. 一个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时,这将被清理一些,因为我们可以将占位符作为兄弟返回到您的子函数,而无需创建包装元素

可拖动 Props

{
  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>
  ));
}

子函数 (渲染 Props )

React一个孩子Draggable必须是返回一个函数ReactElement.

<Draggable draggableId="draggable-1" index={0}>
  {(provided, snapshot) => (
      <div
        ref={provided.innerRef}
        {...provided.draggableProps}
        {...provided.dragHandleProps}
      >
        Drag me!
      </div>
  )}
</Draggable>;

该函数提供了两个参数:

1.提供: (DraggableProvided)

type DraggableProvided = {|
  innerRef: (HTMLElement) => void,
  draggableProps: DraggableProps,
  // will be null if the draggable is disabled
  dragHandleProps: ?DragHandleProps,
|}

提供的对象中的所有内容必须适用于“Draggable”才能正常运行.

看我们的innerRef运用指南innerRef

innerRef: 例子
<Draggable draggableId="draggable-1" index={0}>
  {(provided, snapshot) => <div ref={provided.innerRef}>Drag me!</div>}
</Draggable>;
draggableProps 类型信息
// 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',
|}
draggableProps 例子
<Draggable draggableId="draggable-1" index={0}>
  {(provided, snapshot) => (
    <div ref={provided.innerRef} {...provided.draggableProps}>
      Drag me!
    </div>
  )}
</Draggable>;
定位所有权

它是该库的一个合约,它拥有拖动元素的定位逻辑.

这包括诸如top,right,bottom,lefttransform. 库 可能会改变它是如何定位事物以及它使用的是哪些属性,而不会执行主要的版本冲突. 也建议你不要使用你自己的transition属性添加到拖动元素.

警告: position: fixed

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目的. 也欢迎申请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>;
避免 margin 折叠 多个Draggable

边缘崩溃是CSS中非常困难的部分之一. 对于我们的目的,如果你有一个Draggablemargin-bottom: 10px和下一个Draggable有一个margin-top: 12px这些折叠将会坍方并由此产生的余量将是两者中较大的一个: 12px. 当我们进行我们的计算时,我们目前没有考虑边缘折叠. 如果你确实想在兄弟姐妹身上留下一个空白,那么把它们都包在一个div并将边缘应用于内部div, 所以他们不是直系兄弟姐妹.

Draggable们应该是可见的兄弟姐妹

这是一个假设Draggables是可见的兄弟姐妹彼此之间. 中间还可以有其他元素,但这些元素不应占用额外的空间. 无论如何,你可能不会这样做,但只要说出来就非常清楚.

// 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>
dragHandleProps类型信息
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,
|}
dragHandleProps例子: 标准
<Draggable draggableId="draggable-1" index={0}>
  {(provided, snapshot) => (
    <div
      ref={provided.innerRef}
      {...provided.draggableProps}
      {...provided.dragHandleProps}
    >
      Drag me!
    </div>
  )}
</Draggable>;
dragHandleProps例子: 自定义拖动控制

通过它的一部分控制整个可拖动

<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猴子补丁

你可以重写一些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>;

2.快照: (DraggableStateSnapshot)

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占位符

当拖动一个Draggable我们留下了一个占位符 React.Element保持空间Droppable以防止它崩溃. 占位符模仿样式和布局 (包括width,height,margin,tagNamedisplay) 以确保列表尺寸在拖动时不受影响. 它将被react-beautiful-dnd作为直接兄弟插入由Draggable子函数返回的React.Node

添加一个onClick控制到一个Draggable或者一个拖动控制

欢迎您添加您自己的onClick处理程序Draggable或者一个拖动控制 (这可能是相同的元素) . onClick如果发生点击,事件处理程序将始终被调用. 如果我们阻止点击,那么我们就是event.defaultPrevented财产将被设置为true. 我们阻止发生点击事, 当用户拖动项目时 件. 看到#马虎点击并点击预防🐱🎁 了解更多信息.

交互式的子元素Draggable

这是你的Draggable可能包含交互元素. 默认情况下,我们阻止拖动这些元素. 通过这样做,我们允许这些元素以通常的方式运行. 以下是默认阻止拖动的交互式元素列表:

您可以通过添加选项disableInteractiveElementBlockingDraggable来退出此行为. 但是,您是否应该这样做是值得怀疑的,因为它会使交互式元素无法使用. 如果你需要有条件的阻止交互式元素拖动,可添加disableInteractiveElementBlocking退出默认的阻塞 和 猴子补丁到dragHandleProps (DragHandleProps)事件处理程序禁用拖动.

resetServerContext

resetServerContext函数应该在服务器端呈现 (SSR) 时使用. 它确保 上下文状态 不会在服务器上的多个呈现中持续存在,这会在服务器上呈现多个请求后 导致 客户端/服务器标记 不匹配.

在调用服务器端渲染方法之前使用它:

import { resetServerContext } from 'react-beautiful-dnd';
import { renderToString } from 'react-dom/server';

...

resetServerContext();
renderToString(...);

Flow 使用

react-beautiful-dnd是使用 类型化 的flowtype. 这大大提高了代码库内部的一致性. 我们还公开了许多公共类型,如果您愿意,可以输入您的 javascript 类型. 如果你不使用flowtype这不会阻止你使用该库. 对于那些想要它的人来说,这只是额外的安全.

公共 Flow 类型

// 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,
|}

使用 Flow 类型

这些类型作为模块的一部分导出,因此使用它们非常简单:

import type { DroppableProvided } from 'react-beautiful-dnd';

Typescript

如果你正在使用Typescript你可以使用社区维护DefinitelyTyped 类型定义. 安装说明.

这有些 typescript 的例子.

用 flow 类型示例应用程序

我们创造了一个简单示例它练习 flow . 这是一个超级简单React基于react-create-app. 你可以用它作为参考,看看如何正确设置.

社区作品

其他

工程健康

类型化

这个代码库是用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的!