Skip to content

Latest commit

 

History

History
466 lines (366 loc) · 13.1 KB

README.md

File metadata and controls

466 lines (366 loc) · 13.1 KB

License: MIT

React DnD AX

Motivation

There are many great React Drag and Drop components available on Github. Such as the first search result you will get on google: React Dnd. However, to fulfill the requirements of developing a website that requires full accessibility, web and mobile supports, we still need to write our own components. Therefore, we are sharing this HOC, which provides full accessibility support, can work on mobile and desktop applications, and easy to be integrated with your existing react components.

DEMO

https://artecher.github.io/react-dnd-ax-demo/

As storybook doesn't support mobile view, please visit this site on desktop or create your own project with sources code in this repo locally. We are planning to replace storybook in the future.

Run demo locally

npm install && npm start

The browser will open automaticly and navigate to localhost:3000/ If you wanna see the Complex Example, go to src/index.js, comment line 4-5, and uncomment line 7-8

Features

  • Shipped as High Order Components (HOC) - Which means no pain to be integrated with your existing components
  • freedom on customization - Styles of the list can be fully customized
  • Perfect support for Mobile and Desktop
  • Accessibility support- User is able to work with it purely on keyboard
  • Easy and simply examples to help you adopt

Installation

npm install react-dnd-ax --save

Use a module bundler that supports either ES2016 or CommonJS (webpack, Rollup):

//  Compile ES6 with Babel
import { DragNDropContainer, DragNDropItem } from 'react-dnd-ax'

// Without ES6 compiler
var Sortable = require('react-dnd-ax')
var SortableContainer = Sortable.DragNDropContainer
var SortableItem = Sortable.DragNDropItem

If you don't like Module Bundler, you can simply load UMD and style file

<link href="react-dnd-ax/dist/umd/bundle.css" />
<script src="react-dnd-ax/dist/umd/react-dnd-ax.js"></script>

If you want to use compressed JS file (compressed by UglifyJs,no sourceMap):;

<script src="react-dnd-ax/dist/umd/react-dnd-ax.min.js"></script>

Usage

IMPORTANT!Need to import react-dnd-ax.css into your html page

run examples locally

npm install
npm run storybook

then go to http://localhost:9001 in your browser to see examples

Optionally, run examples in Docker

docker-compose up

Stop docker examples and clean up

^C
docker-compose down

Basic Example

import React from 'react'
import {Icon} from 'react-fa'

import {DragNDropContainer, DragNDropItem} from '../../react-dnd-ax'
import {basicItems} from '../data'

import '../styles/common.scss'
import './basic-example.scss'

class BasicExample extends React.Component {
  state = {
    items: basicItems,
  }

  onReorderLinks = (newItems) => {
    this.setState({
      items: [...newItems]
    })
  }

  render() {
    const BasicItem = DragNDropItem(({item, itemRef, dragPointRef}) => {
      return (
        <div
          className="item-row"
          ref={itemRef} // mandatory: put this attribute to the container element of the movable item
        >
            <span className="text">{item.text}</span>
            <button
              ref={dragPointRef} // mandatory: put this attribute to the drag handler
              className="drag-point"
              draggable // mandatory HTML attribute for drag handler
              tabIndex="0" // mandatory HTML attribute, make it possible to focus on the drag handler
              title="Drag this link to reorder the item" // AX title
            >
              <Icon name="arrows"/>
            </button>
        </div>
      )
    })

    const BasicList = DragNDropContainer((props) => {
      return (
        <div id="modules-section">
          {
            props.items.map((item, index) => {
              return <BasicItem
                item={item}
                index={index} // mandatory: give index to the DragNDropItem HOC
                key={item.id}
                preview={<div>{item.text}</div>} // customize your preview
                {...props} // mandatory: need to pass down the props
              />
            })
          }
        </div>
      )
    })

    return (
      <div id="basic-container" className="container">
        <BasicList
          items={this.state.items}
          boundingElementId="container"
          onReorderItem={this.onReorderLinks}
          scrollContainerId="basic-container"
        />
      </div>
    )
  }
}

export default BasicExample

Complex Example (integration with existing components)

import React from 'react'

import { DragNDropContainer, DragNDropItem } from '../../react-dnd-ax'
import { countries } from '../data'
import Country from './Country'

import '../styles/common.scss'
import './ComplexExample.scss'

class ComplexExample extends React.Component {
  state = {
    countries: countries,
  }

  onReorderCountries = (newOrderCountries) => {
    this.setState({
      countries: [...newOrderCountries]
    })
  }

  render() {
    const ModuleItem = DragNDropItem(Country) // Country is your existing component

    const CountryList = DragNDropContainer((props) => {
      return (
        <div>
          {
            props.items.map((country, index) => {
              return <ModuleItem
                country={country}
                index={index} // mandatory: give index to the DragNDropItem HOC
                key={country.name}
                preview={<span>{country.name}</span>} // customize your preview
                {...props} // mandatory: need to pass down the props
              />
            })
          }
        </div>
      )
    })

    return (
      <div id="complex-container" className="container">
        <CountryList
          items={this.state.countries}
          onReorderItem={this.onReorderCountries}
          scrollContainerId="complex-container"
        />
      </div>
    )
  }
}

export default ComplexExample

PropTypes

DragNDropItem

Prop Type Description
index number the index value of a single item
preview React Element the preview html element when dragging the movable item

DragNDropContainer

Prop Type Description
items Array the array consists of movable items
onReorderItem(reorderedItems, sourceDragItem) Function the callback function triggered by dropping a movable item
scrollContainerId String the container id of the drag and drop component (usage refer to examples)
boundingElementId String Id of anchor element for positioning drag and drop preview item if an ancestor element's styling prevents fixed position (optional)

中文文档

动机

Github 上有非常多优秀的 React DnD 的 组件,尤其是你在 google 上会遇到的第一个搜索结果:React Dnd, 但是在业务中,我相信很多开发者也遇到和我们相同的问题:那就是只需要一个可以拖动重新排序的列表,经过一段时间的调查,我们发现要满足支持多端,高度的自定义,同时还有完美的可访问性支持的组件还不存在,所以就写了这个高阶组件希望分享给有同样需求的开发者。

特性

  • 高阶组件 (HOC) - 可以和你现有的组件完美融和
  • 高度自定义 - 列表的样式和结构完全可控
  • 完美支持桌面和移动端
  • 完美支持 Accessibility(可访问性)- 可以用键盘完成一切操作
  • 简单易用,容易上手

安装

npm install react-dnd-ax --save

使用一个支持 ES2015模块 或者 CommonJs 的工具(webpack, Rollup)来引用:

//  使用 Babel 来编译 ES6
import { DragNDropContainer, DragNDropItem } from 'react-dnd-ax'

// 不使用 ES6 编译器
var Sortable = require('react-dnd-ax')
var SortableContainer = Sortable.DragNDropContainer
var SortableItem = Sortable.DragNDropItem

如果你不使用 Module Bundler, 可以直接加载 UMD 格式的文件,同时需要加载样式文件。

<link href="react-dnd-ax/dist/umd/bundle.css" />
<script src="react-dnd-ax/dist/umd/react-dnd-ax.js"></script>

如果你想使用压缩过的 JS 文件,(使用 UglifyJs 压缩,没有 sourceMap),那么可以引用:

<script src="react-dnd-ax/dist/umd/react-dnd-ax.min.js"></script>

使用

切记!将安装包里面的 react-dnd-ax.css 引入项目的html页面

本地运行例子

npm install
npm run storybook

then go to http://localhost:9001 in your browser to see examples

如果你安装了Docker,你也可以在Docker中运行例子

docker-compose up

停止Docker,并做清理

^C
docker-compose down

简单示例

import React from 'react'
import {Icon} from 'react-fa'

import {DragNDropContainer, DragNDropItem} from '../../react-dnd-ax'
import {basicItems} from '../data'

import '../styles/common.scss'
import './basic-example.scss'

class BasicExample extends React.Component {
  state = {
    items: basicItems,
  }

  onReorderLinks = (newItems) => {
    this.setState({
      items: [...newItems]
    })
  }

  render() {
    const BasicItem = DragNDropItem(({item, itemRef, dragPointRef}) => {
      return (
        <div
          className="item-row"
          ref={itemRef} // mandatory: put this attribute to the container element of the movable item
        >
            <span className="text">{item.text}</span>
            <button
              ref={dragPointRef} // mandatory: put this attribute to the drag handler
              className="drag-point"
              draggable // mandatory HTML attribute for drag handler
              tabIndex="0" // mandatory HTML attribute, make it possible to focus on the drag handler
              title="Drag this link to reorder the item" // AX title
            >
              <Icon name="arrows"/>
            </button>
        </div>
      )
    })

    const BasicList = DragNDropContainer((props) => {
      return (
        <div id="modules-section">
          {
            props.items.map((item, index) => {
              return <BasicItem
                item={item}
                index={index} // mandatory: give index to the DragNDropItem HOC
                key={item.id}
                preview={<div>{item.text}</div>} // customize your preview
                {...props} // mandatory: need to pass down the props
              />
            })
          }
        </div>
      )
    })

    return (
      <div id="basic-container" className="container">
        <BasicList
          items={this.state.items}
          boundingElementId="container"
          onReorderItem={this.onReorderLinks}
          scrollContainerId="basic-container"
        />
      </div>
    )
  }
}

export default BasicExample

复杂示例 (和已有component集成)

import React from 'react'

import { DragNDropContainer, DragNDropItem } from '../../react-dnd-ax'
import { countries } from '../data'
import Country from './Country'

import '../styles/common.scss'
import './ComplexExample.scss'

class ComplexExample extends React.Component {
  state = {
    countries: countries,
  }

  onReorderCountries = (newOrderCountries) => {
    this.setState({
      countries: [...newOrderCountries]
    })
  }

  render() {
    const ModuleItem = DragNDropItem(Country) // Country is your existing component

    const CountryList = DragNDropContainer((props) => {
      return (
        <div>
          {
            props.items.map((country, index) => {
              return <ModuleItem
                country={country}
                index={index} // mandatory: give index to the DragNDropItem HOC
                key={country.name}
                preview={<span>{country.name}</span>} // customize your preview
                {...props} // mandatory: need to pass down the props
              />
            })
          }
        </div>
      )
    })

    return (
      <div id="complex-container" className="container">
        <CountryList
          items={this.state.countries}
          onReorderItem={this.onReorderCountries}
          scrollContainerId="complex-container"
        />
      </div>
    )
  }
}

export default ComplexExample

PropTypes

DragNDropItem

属性 类型 描述
index number 当前可移动条目的索引值
preview React Element 移动条目是的预览html元素

DragNDropContainer

属性 类型 描述
items Array 由可移动的条目组成的数组
onReorderItem(reorderedItems, sourceDragItem) Function 当条目被移动时被触发的回掉函数
scrollContainerId String drag and drop component的container的id (具体用法见示例)
boundingElementId String Id of anchor element for positioning drag and drop preview item if an ancestor element's styling prevents fixed position (可选) 有一些浏览器,如Chrome,当父元素有translateZ(0)样式时,会导致preview item得不到正确的y轴的值。这个参与用于修正这个问题。