├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .travis.yml
├── LICENSE.md
├── README.md
├── examples
├── dragColumn.html
├── dragColumn.js
├── dragColumn.less
├── index.less
├── simple.html
└── simple.js
├── index.d.ts
├── package.json
├── plugins
└── DragDropTouch.js
└── src
├── ReactDragColumnView.jsx
├── ReactDragListView.jsx
├── index.js
└── util.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [Makefile]
16 | indent_style = tab
17 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raisezhang/react-drag-listview/36aec4a875267ff8d59b79dd48aed3c19db4ec23/.eslintignore
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-airbnb",
3 | "rules": {
4 | "no-useless-computed-key": 0,
5 | "strict": 0,
6 | "func-names": 0,
7 | "space-before-function-paren": [0, "always"],
8 |
9 | "no-var": 0,
10 | "vars-on-top": 0,
11 |
12 | "comma-dangle": 0,
13 | "consistent-return": 1,
14 | "no-return-assign": 0,
15 | "no-extend-native": 1,
16 | "import/no-extraneous-dependencies": 0,
17 | "import/no-unresolved": 0,
18 | "import/extensions": 0,
19 | "react/no-array-index-key": 0,
20 | "jsx-a11y/anchor-is-valid": 0,
21 | "react/jsx-filename-extension": 0
22 | },
23 | "globals": {
24 | "document": true,
25 | "window": true
26 | },
27 | "parser": "babel-eslint"
28 | }
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | tmp
3 | node_modules
4 | lib
5 | dist
6 | coverage
7 | npm-debug.log*
8 | build
9 | package-lock.json
10 | es/
11 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - "6"
5 | - "7"
6 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 raisezhang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-drag-listview
2 |
3 | React drag list component.
4 |
5 | [![NPM version][npm-image]][npm-url]
6 | [![build status][travis-image]][travis-url]
7 | [![npm download][download-image]][download-url]
8 |
9 | [npm-image]: http://img.shields.io/npm/v/react-drag-listview.svg?style=flat-square
10 | [npm-url]: http://npmjs.org/package/react-drag-listview
11 | [travis-image]: https://img.shields.io/travis/raisezhang/react-drag-listview.svg?style=flat-square
12 | [travis-url]: https://travis-ci.org/raisezhang/react-drag-listview
13 | [download-image]: https://img.shields.io/npm/dm/react-drag-listview.svg?style=flat-square
14 | [download-url]: https://npmjs.org/package/react-drag-listview
15 |
16 | ## install
17 |
18 | [](https://npmjs.org/package/react-drag-listview)
19 |
20 |
21 | ## Use on mobile (touch) devices
22 |
23 | * `react-drag-listview` already supports mobile (touch) devices, which can be easily implemented based on the [dragdroptouch](https://github.com/Bernardo-Castilho/dragdroptouch) polyfill
24 | * Need to manually add polyfill `dragdroptouch` to your website. e.g.
25 | ```html
26 |
27 | ```
28 | * Example: [Drag on mobile devices](https://codepen.io/raisezhang/pen/wvpVbQO)
29 |
30 | ## Example
31 |
32 | * ###### Drag Rows
33 | * [Simple dragging demo](https://raisezhang.github.io/react-drag-listview/examples/simple.html)
34 | * [Dragging Ant-Design table](https://codepen.io/raisezhang/pen/MmjypX)
35 | * [Dragging Ant-Design table width expanded rows](https://codepen.io/raisezhang/pen/OrrGJL)
36 | * [Dragging Ant-Design transfer items](https://codepen.io/raisezhang/pen/rNdGEzN)
37 | * [Dragging Ant-Design Nested List Items](https://codesandbox.io/s/react-drag-listview-nested-drag-example-mdrbh?file=/src/questions.js)
38 |
39 | * ###### Drag Columns
40 | * [Simple dragging columns demo](https://raisezhang.github.io/react-drag-listview/examples/dragColumn.html)
41 | * [Dragging Ant-Design table columns](https://codepen.io/raisezhang/pen/MoMoyz)
42 |
43 | ## Development
44 |
45 | ```bash
46 | npm install
47 | npm start
48 | ```
49 |
50 | ## Usage
51 |
52 | ```javascript
53 | const ReactDragListView = require('react-drag-listview');
54 |
55 | class Demo extends React.Component {
56 | constructor(props) {
57 | super(props);
58 |
59 | const data = [];
60 | for (let i = 1, len = 7; i < len; i++) {
61 | data.push({
62 | title: `rows${i}`
63 | });
64 | }
65 |
66 | this.state = {
67 | data
68 | };
69 | }
70 |
71 | render() {
72 | const that = this;
73 | const dragProps = {
74 | onDragEnd(fromIndex, toIndex) {
75 | const data = [...that.state.data];
76 | const item = data.splice(fromIndex, 1)[0];
77 | data.splice(toIndex, 0, item);
78 | that.setState({ data });
79 | },
80 | nodeSelector: 'li',
81 | handleSelector: 'a'
82 | };
83 |
84 | return (
85 |
86 |
87 | {this.state.data.map((item, index) => (
88 | -
89 | {item.title}
90 | Drag
91 |
92 | ))}
93 |
94 |
95 | );
96 | }
97 | }
98 |
99 | ```
100 |
101 | ## API
102 |
103 | ### Properties
104 |
105 |
106 |
107 |
108 | Name |
109 | Type |
110 | Default |
111 | Description |
112 |
113 |
114 |
115 |
116 | onDragEnd |
117 | Function(fromIndex, toIndex) |
118 | |
119 | on drag end callback, required |
120 |
121 |
122 | nodeSelector |
123 | String |
124 | tr |
125 | get drag item cssQuery |
126 |
127 |
128 | handleSelector |
129 | String |
130 | nodeSelector |
131 | get drag handle cssQuery |
132 |
133 |
134 | ignoreSelector |
135 | String |
136 | |
137 | ignore node list |
138 |
139 |
140 | enableScroll |
141 | Boolean |
142 | true |
143 | whether use auto scroll for dragging |
144 |
145 |
146 | scrollSpeed |
147 | Number |
148 | 10 |
149 | scroll speed |
150 |
151 |
152 | lineClassName |
153 | String |
154 | |
155 | get dragLine's className, css properties must be use !important |
156 |
157 |
158 |
159 |
160 | ## License
161 |
162 | react-drag-listview is released under the MIT license.
163 |
--------------------------------------------------------------------------------
/examples/dragColumn.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raisezhang/react-drag-listview/36aec4a875267ff8d59b79dd48aed3c19db4ec23/examples/dragColumn.html
--------------------------------------------------------------------------------
/examples/dragColumn.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console,func-names,react/no-multi-comp */
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import ReactDragListView from 'react-drag-listview/src/index.js';
5 |
6 | import './index.less';
7 | import './dragColumn.less';
8 |
9 | // Import only if you need to support touch screen devices
10 | // https://bernardo-castilho.github.io/DragDropTouch/DragDropTouch.js
11 | import '../plugins/DragDropTouch.js';
12 |
13 | const { DragColumn } = ReactDragListView;
14 |
15 | class Demo extends React.Component {
16 | constructor(props) {
17 | super(props);
18 | const data = [];
19 | for (let i = 1, len = 21; i < len; i += 1) {
20 | data.push({
21 | title: `col${i}`
22 | });
23 | }
24 | this.state = {
25 | data
26 | };
27 | }
28 |
29 | render() {
30 | const that = this;
31 | const dragProps = {
32 | onDragEnd(fromIndex, toIndex) {
33 | const data = [...that.state.data];
34 | const item = data.splice(fromIndex, 1)[0];
35 | data.splice(toIndex, 0, item);
36 | that.setState({ data });
37 | },
38 | nodeSelector: 'li',
39 | handleSelector: 'a'
40 | };
41 |
42 | return (
43 |
44 |
Dragging columns
45 |
46 |
47 |
48 | {this.state.data.map((item, index) => (
49 | -
50 | {item.title}
51 | Drag
52 |
53 | ))}
54 |
55 |
56 |
57 |
58 | );
59 | }
60 | }
61 |
62 | ReactDOM.render(, document.getElementById('__react-content'));
63 |
--------------------------------------------------------------------------------
/examples/dragColumn.less:
--------------------------------------------------------------------------------
1 | .simple2 .simple-inner {
2 | height: auto;
3 | }
4 |
5 | .simple2 ol li {
6 | border-right: solid 1px #ddd;
7 | border-bottom: 0 none;
8 | padding: 30px 0;
9 | position: relative;
10 | transition: all ease 0.3s;
11 | float: left;
12 | height: 368px;
13 | box-sizing: border-box;
14 | width: 70px;
15 | text-align: center;
16 | display: block;
17 | }
18 |
19 | .simple2 ol::after {
20 | content: '.';
21 | display: block;
22 | visibility: hidden;
23 | height: 0;
24 | line-height: 0;
25 | font-size: 0;
26 | clear: both;
27 | }
28 |
29 | .simple2 ol li:nth-last-child(1) {
30 | border-right: 0 none;
31 | }
32 |
33 | .simple2 ol li a {
34 | position: static;
35 | display: block;
36 | margin-top: 20px;
37 | }
38 |
--------------------------------------------------------------------------------
/examples/index.less:
--------------------------------------------------------------------------------
1 | .simple {
2 | background: #f5f5f5;
3 | padding: 10px 20px 20px;
4 | border-radius: 5px;
5 | }
6 |
7 | .simple h2 {
8 | margin: 0;
9 | padding: 10px 0;
10 | }
11 |
12 | .simple a,
13 | .simple a:active,
14 | .simple a:hover,
15 | .simple a:visited {
16 | color: #22abed;
17 | }
18 |
19 | ol,
20 | ul {
21 | list-style-type: none;
22 | padding: 0;
23 | margin: 0;
24 | }
25 |
26 | .simple1 ol {
27 | background: #fff;
28 | }
29 |
30 | .simple1 ol li {
31 | border-bottom: solid 1px #ddd;
32 | padding: 8px 16px;
33 | min-height: 60px;
34 | position: relative;
35 | transition: all ease 0.3s;
36 | display: flex;
37 | align-items: center;
38 | }
39 |
40 | .simple1 ol li:hover {
41 | background: rgba(34, 171, 237, 0.1);
42 | }
43 |
44 | .simple1 ol li a {
45 | position: absolute;
46 | right: 20px;
47 | cursor: move;
48 | user-select: none;
49 | padding: 10px;
50 | }
51 |
52 | .simple1 ol li:nth-last-child(1) {
53 | border-bottom: 0 none;
54 | }
55 |
56 | .simple1 .simple-inner {
57 | max-width: 600px;
58 | overflow: hidden;
59 | border: solid 1px #ddd;
60 | border-radius: 5px;
61 | height: 400px;
62 | overflow: auto;
63 | margin: 0 auto;
64 | }
65 |
--------------------------------------------------------------------------------
/examples/simple.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/raisezhang/react-drag-listview/36aec4a875267ff8d59b79dd48aed3c19db4ec23/examples/simple.html
--------------------------------------------------------------------------------
/examples/simple.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console,func-names,react/no-multi-comp */
2 | import React from 'react';
3 | import ReactDOM from 'react-dom';
4 | import ReactDragListView from 'react-drag-listview/src/index.js';
5 |
6 | import './index.less';
7 |
8 | // Import only if you need to support touch screen devices
9 | // https://bernardo-castilho.github.io/DragDropTouch/DragDropTouch.js
10 | import '../plugins/DragDropTouch.js';
11 |
12 | class Demo extends React.Component {
13 | constructor(props) {
14 | super(props);
15 | const data = [];
16 | for (let i = 1, len = 21; i < len; i += 1) {
17 | data.push({
18 | title: `rows${i}`
19 | });
20 | }
21 | this.state = {
22 | data
23 | };
24 | }
25 |
26 | render() {
27 | const that = this;
28 | const dragProps = {
29 | onDragEnd(fromIndex, toIndex) {
30 | const data = [...that.state.data];
31 | const item = data.splice(fromIndex, 1)[0];
32 | data.splice(toIndex, 0, item);
33 | that.setState({ data });
34 | },
35 | nodeSelector: 'li',
36 | handleSelector: 'a'
37 | };
38 |
39 | return (
40 |
41 |
Dragging handle
42 |
43 |
44 |
45 | {this.state.data.map((item, index) => (
46 | -
47 | {item.title}
48 | Start Drag
49 |
50 | ))}
51 |
52 |
53 |
54 |
55 | );
56 | }
57 | }
58 |
59 | ReactDOM.render(, document.getElementById('__react-content'));
60 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from 'react';
2 |
3 | export interface DragListViewProps {
4 | // on drag end callback, required
5 | onDragEnd: (fromIndex: number, toIndex: number) => void;
6 | // get drag handle cssQuery
7 | handleSelector?: string | undefined;
8 | // get drag item cssQuery
9 | nodeSelector?: string | undefined;
10 | // ignore node list
11 | ignoreSelector?: string | undefined;
12 | // whether use auto scroll for dragging
13 | enableScroll?: boolean | undefined;
14 | // scroll speed
15 | scrollSpeed?: number | undefined;
16 | // get dragLine's className, css properties must be use !important
17 | lineClassName?: string | undefined;
18 | // children
19 | children?: React.ReactNode
20 | }
21 |
22 | declare class ReactDragListView extends React.Component {
23 | }
24 |
25 | declare class ReactDragColumnView extends ReactDragListView {
26 | }
27 |
28 | declare const DragListView: typeof ReactDragListView & { DragColumn: typeof ReactDragColumnView };
29 | export default DragListView;
30 |
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-drag-listview",
3 | "version": "2.0.0",
4 | "author": "raisezhang@hotmail.com",
5 | "description": "drag list view and table view component for react",
6 | "keywords": [
7 | "react",
8 | "react-component",
9 | "sortable",
10 | "antd",
11 | "rc-table",
12 | "react-drag",
13 | "react-drag-table",
14 | "react-drag-list"
15 | ],
16 | "license": "MIT",
17 | "homepage": "https://github.com/raisezhang/react-drag-listview#readme",
18 | "main": "./lib/index",
19 | "module": "./es/index",
20 | "types": "./index.d.ts",
21 | "files": [
22 | "assets/*.css",
23 | "dist",
24 | "es",
25 | "lib",
26 | "index.d.ts"
27 | ],
28 | "maintainers": [
29 | "raisezhang@hotmail.com"
30 | ],
31 | "repository": {
32 | "type": "git",
33 | "url": "git+https://github.com/raisezhang/react-drag-listview.git"
34 | },
35 | "bugs": {
36 | "url": "https://github.com/raisezhang/react-drag-listview/issues"
37 | },
38 | "config": {
39 | "entry": {
40 | "react-drag-listview": [
41 | "./src/index.js"
42 | ]
43 | },
44 | "port": 8000
45 | },
46 | "scripts": {
47 | "dist": "rc-tools run dist",
48 | "build": "rc-tools run build",
49 | "compile": "rc-tools run compile --babel-runtime",
50 | "gh-pages": "rc-tools run gh-pages",
51 | "start": "rc-tools run server",
52 | "pub": "rc-tools run pub --babel-runtime",
53 | "lint": "rc-tools run lint",
54 | "lint:fix": "rc-tools run lint --fix"
55 | },
56 | "dependencies": {
57 | "babel-runtime": "^6.26.0",
58 | "prop-types": "^15.5.8"
59 | },
60 | "devDependencies": {
61 | "expect.js": "~0.3.1",
62 | "pre-commit": "1.x",
63 | "rc-tools": "^8.2.2",
64 | "react": "^16.0.0",
65 | "react-dom": "^16.0.0"
66 | },
67 | "pre-commit": [
68 | "lint"
69 | ]
70 | }
71 |
--------------------------------------------------------------------------------
/plugins/DragDropTouch.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var DragDropTouch;
3 | (function (DragDropTouch_1) {
4 | 'use strict';
5 | /**
6 | * Object used to hold the data that is being dragged during drag and drop operations.
7 | *
8 | * It may hold one or more data items of different types. For more information about
9 | * drag and drop operations and data transfer objects, see
10 | * HTML Drag and Drop API.
11 | *
12 | * This object is created automatically by the @see:DragDropTouch singleton and is
13 | * accessible through the @see:dataTransfer property of all drag events.
14 | */
15 | var DataTransfer = (function () {
16 | function DataTransfer() {
17 | this._dropEffect = 'move';
18 | this._effectAllowed = 'all';
19 | this._data = {};
20 | }
21 | Object.defineProperty(DataTransfer.prototype, "dropEffect", {
22 | /**
23 | * Gets or sets the type of drag-and-drop operation currently selected.
24 | * The value must be 'none', 'copy', 'link', or 'move'.
25 | */
26 | get: function () {
27 | return this._dropEffect;
28 | },
29 | set: function (value) {
30 | this._dropEffect = value;
31 | },
32 | enumerable: true,
33 | configurable: true
34 | });
35 | Object.defineProperty(DataTransfer.prototype, "effectAllowed", {
36 | /**
37 | * Gets or sets the types of operations that are possible.
38 | * Must be one of 'none', 'copy', 'copyLink', 'copyMove', 'link',
39 | * 'linkMove', 'move', 'all' or 'uninitialized'.
40 | */
41 | get: function () {
42 | return this._effectAllowed;
43 | },
44 | set: function (value) {
45 | this._effectAllowed = value;
46 | },
47 | enumerable: true,
48 | configurable: true
49 | });
50 | Object.defineProperty(DataTransfer.prototype, "types", {
51 | /**
52 | * Gets an array of strings giving the formats that were set in the @see:dragstart event.
53 | */
54 | get: function () {
55 | return Object.keys(this._data);
56 | },
57 | enumerable: true,
58 | configurable: true
59 | });
60 | /**
61 | * Removes the data associated with a given type.
62 | *
63 | * The type argument is optional. If the type is empty or not specified, the data
64 | * associated with all types is removed. If data for the specified type does not exist,
65 | * or the data transfer contains no data, this method will have no effect.
66 | *
67 | * @param type Type of data to remove.
68 | */
69 | DataTransfer.prototype.clearData = function (type) {
70 | if (type != null) {
71 | delete this._data[type.toLowerCase()];
72 | }
73 | else {
74 | this._data = {};
75 | }
76 | };
77 | /**
78 | * Retrieves the data for a given type, or an empty string if data for that type does
79 | * not exist or the data transfer contains no data.
80 | *
81 | * @param type Type of data to retrieve.
82 | */
83 | DataTransfer.prototype.getData = function (type) {
84 | return this._data[type.toLowerCase()] || '';
85 | };
86 | /**
87 | * Set the data for a given type.
88 | *
89 | * For a list of recommended drag types, please see
90 | * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Recommended_Drag_Types.
91 | *
92 | * @param type Type of data to add.
93 | * @param value Data to add.
94 | */
95 | DataTransfer.prototype.setData = function (type, value) {
96 | this._data[type.toLowerCase()] = value;
97 | };
98 | /**
99 | * Set the image to be used for dragging if a custom one is desired.
100 | *
101 | * @param img An image element to use as the drag feedback image.
102 | * @param offsetX The horizontal offset within the image.
103 | * @param offsetY The vertical offset within the image.
104 | */
105 | DataTransfer.prototype.setDragImage = function (img, offsetX, offsetY) {
106 | var ddt = DragDropTouch._instance;
107 | ddt._imgCustom = img;
108 | ddt._imgOffset = { x: offsetX, y: offsetY };
109 | };
110 | return DataTransfer;
111 | }());
112 | DragDropTouch_1.DataTransfer = DataTransfer;
113 | /**
114 | * Defines a class that adds support for touch-based HTML5 drag/drop operations.
115 | *
116 | * The @see:DragDropTouch class listens to touch events and raises the
117 | * appropriate HTML5 drag/drop events as if the events had been caused
118 | * by mouse actions.
119 | *
120 | * The purpose of this class is to enable using existing, standard HTML5
121 | * drag/drop code on mobile devices running IOS or Android.
122 | *
123 | * To use, include the DragDropTouch.js file on the page. The class will
124 | * automatically start monitoring touch events and will raise the HTML5
125 | * drag drop events (dragstart, dragenter, dragleave, drop, dragend) which
126 | * should be handled by the application.
127 | *
128 | * For details and examples on HTML drag and drop, see
129 | * https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations.
130 | */
131 | var DragDropTouch = (function () {
132 | /**
133 | * Initializes the single instance of the @see:DragDropTouch class.
134 | */
135 | function DragDropTouch() {
136 | this._lastClick = 0;
137 | // enforce singleton pattern
138 | if (DragDropTouch._instance) {
139 | throw 'DragDropTouch instance already created.';
140 | }
141 | // detect passive event support
142 | // https://github.com/Modernizr/Modernizr/issues/1894
143 | var supportsPassive = false;
144 | document.addEventListener('test', function () { }, {
145 | get passive() {
146 | supportsPassive = true;
147 | return true;
148 | }
149 | });
150 | // listen to touch events
151 | if (navigator.maxTouchPoints) {
152 | var d = document,
153 | ts = this._touchstart.bind(this),
154 | tm = this._touchmove.bind(this),
155 | te = this._touchend.bind(this),
156 | opt = supportsPassive ? { passive: false, capture: false } : false;
157 | d.addEventListener('touchstart', ts, opt);
158 | d.addEventListener('touchmove', tm, opt);
159 | d.addEventListener('touchend', te);
160 | d.addEventListener('touchcancel', te);
161 | }
162 | }
163 | /**
164 | * Gets a reference to the @see:DragDropTouch singleton.
165 | */
166 | DragDropTouch.getInstance = function () {
167 | return DragDropTouch._instance;
168 | };
169 | // ** event handlers
170 | DragDropTouch.prototype._touchstart = function (e) {
171 | var _this = this;
172 | if (this._shouldHandle(e)) {
173 | // raise double-click and prevent zooming
174 | if (Date.now() - this._lastClick < DragDropTouch._DBLCLICK) {
175 | if (this._dispatchEvent(e, 'dblclick', e.target)) {
176 | e.preventDefault();
177 | this._reset();
178 | return;
179 | }
180 | }
181 | // clear all variables
182 | this._reset();
183 | // get nearest draggable element
184 | var src = this._closestDraggable(e.target);
185 | if (src) {
186 | // give caller a chance to handle the hover/move events
187 | if (!this._dispatchEvent(e, 'mousemove', e.target) &&
188 | !this._dispatchEvent(e, 'mousedown', e.target)) {
189 | // get ready to start dragging
190 | this._dragSource = src;
191 | this._ptDown = this._getPoint(e);
192 | this._lastTouch = e;
193 | e.preventDefault();
194 | // show context menu if the user hasn't started dragging after a while
195 | setTimeout(function () {
196 | if (_this._dragSource == src && _this._img == null) {
197 | if (_this._dispatchEvent(e, 'contextmenu', src)) {
198 | _this._reset();
199 | }
200 | }
201 | }, DragDropTouch._CTXMENU);
202 | if (DragDropTouch._ISPRESSHOLDMODE) {
203 | this._pressHoldInterval = setTimeout(function () {
204 | _this._isDragEnabled = true;
205 | _this._touchmove(e);
206 | }, DragDropTouch._PRESSHOLDAWAIT);
207 | }
208 | }
209 | }
210 | }
211 | };
212 | DragDropTouch.prototype._touchmove = function (e) {
213 | if (this._shouldCancelPressHoldMove(e)) {
214 | this._reset();
215 | return;
216 | }
217 | if (this._shouldHandleMove(e) || this._shouldHandlePressHoldMove(e)) {
218 | // see if target wants to handle move
219 | var target = this._getTarget(e);
220 | if (this._dispatchEvent(e, 'mousemove', target)) {
221 | this._lastTouch = e;
222 | e.preventDefault();
223 | return;
224 | }
225 | // start dragging
226 | if (this._dragSource && !this._img && this._shouldStartDragging(e)) {
227 | this._dispatchEvent(e, 'dragstart', this._dragSource);
228 | this._createImage(e);
229 | this._dispatchEvent(e, 'dragenter', target);
230 | }
231 | // continue dragging
232 | if (this._img) {
233 | this._lastTouch = e;
234 | e.preventDefault(); // prevent scrolling
235 | this._dispatchEvent(e, 'drag', this._dragSource);
236 | if (target != this._lastTarget) {
237 | this._dispatchEvent(this._lastTouch, 'dragleave', this._lastTarget);
238 | this._dispatchEvent(e, 'dragenter', target);
239 | this._lastTarget = target;
240 | }
241 | this._moveImage(e);
242 | this._isDropZone = this._dispatchEvent(e, 'dragover', target);
243 | }
244 | }
245 | };
246 | DragDropTouch.prototype._touchend = function (e) {
247 | if (this._shouldHandle(e)) {
248 | // see if target wants to handle up
249 | if (this._dispatchEvent(this._lastTouch, 'mouseup', e.target)) {
250 | e.preventDefault();
251 | return;
252 | }
253 | // user clicked the element but didn't drag, so clear the source and simulate a click
254 | if (!this._img) {
255 | this._dragSource = null;
256 | this._dispatchEvent(this._lastTouch, 'click', e.target);
257 | this._lastClick = Date.now();
258 | }
259 | // finish dragging
260 | this._destroyImage();
261 | if (this._dragSource) {
262 | if (e.type.indexOf('cancel') < 0 && this._isDropZone) {
263 | this._dispatchEvent(this._lastTouch, 'drop', this._lastTarget);
264 | }
265 | this._dispatchEvent(this._lastTouch, 'dragend', this._dragSource);
266 | this._reset();
267 | }
268 | }
269 | };
270 | // ** utilities
271 | // ignore events that have been handled or that involve more than one touch
272 | DragDropTouch.prototype._shouldHandle = function (e) {
273 | return e &&
274 | !e.defaultPrevented &&
275 | e.touches && e.touches.length < 2;
276 | };
277 |
278 | // use regular condition outside of press & hold mode
279 | DragDropTouch.prototype._shouldHandleMove = function (e) {
280 | return !DragDropTouch._ISPRESSHOLDMODE && this._shouldHandle(e);
281 | };
282 |
283 | // allow to handle moves that involve many touches for press & hold
284 | DragDropTouch.prototype._shouldHandlePressHoldMove = function (e) {
285 | return DragDropTouch._ISPRESSHOLDMODE &&
286 | this._isDragEnabled && e && e.touches && e.touches.length;
287 | };
288 |
289 | // reset data if user drags without pressing & holding
290 | DragDropTouch.prototype._shouldCancelPressHoldMove = function (e) {
291 | return DragDropTouch._ISPRESSHOLDMODE && !this._isDragEnabled &&
292 | this._getDelta(e) > DragDropTouch._PRESSHOLDMARGIN;
293 | };
294 |
295 | // start dragging when specified delta is detected
296 | DragDropTouch.prototype._shouldStartDragging = function (e) {
297 | var delta = this._getDelta(e);
298 | return delta > DragDropTouch._THRESHOLD ||
299 | (DragDropTouch._ISPRESSHOLDMODE && delta >= DragDropTouch._PRESSHOLDTHRESHOLD);
300 | }
301 |
302 | // clear all members
303 | DragDropTouch.prototype._reset = function () {
304 | this._destroyImage();
305 | this._dragSource = null;
306 | this._lastTouch = null;
307 | this._lastTarget = null;
308 | this._ptDown = null;
309 | this._isDragEnabled = false;
310 | this._isDropZone = false;
311 | this._dataTransfer = new DataTransfer();
312 | clearInterval(this._pressHoldInterval);
313 | };
314 | // get point for a touch event
315 | DragDropTouch.prototype._getPoint = function (e, page) {
316 | if (e && e.touches) {
317 | e = e.touches[0];
318 | }
319 | return { x: page ? e.pageX : e.clientX, y: page ? e.pageY : e.clientY };
320 | };
321 | // get distance between the current touch event and the first one
322 | DragDropTouch.prototype._getDelta = function (e) {
323 | if (DragDropTouch._ISPRESSHOLDMODE && !this._ptDown) { return 0; }
324 | var p = this._getPoint(e);
325 | return Math.abs(p.x - this._ptDown.x) + Math.abs(p.y - this._ptDown.y);
326 | };
327 | // get the element at a given touch event
328 | DragDropTouch.prototype._getTarget = function (e) {
329 | var pt = this._getPoint(e), el = document.elementFromPoint(pt.x, pt.y);
330 | while (el && getComputedStyle(el).pointerEvents == 'none') {
331 | el = el.parentElement;
332 | }
333 | return el;
334 | };
335 | // create drag image from source element
336 | DragDropTouch.prototype._createImage = function (e) {
337 | // just in case...
338 | if (this._img) {
339 | this._destroyImage();
340 | }
341 | // create drag image from custom element or drag source
342 | var src = this._imgCustom || this._dragSource;
343 | this._img = src.cloneNode(true);
344 | this._copyStyle(src, this._img);
345 | this._img.style.top = this._img.style.left = '-9999px';
346 | // if creating from drag source, apply offset and opacity
347 | if (!this._imgCustom) {
348 | var rc = src.getBoundingClientRect(), pt = this._getPoint(e);
349 | this._imgOffset = { x: pt.x - rc.left, y: pt.y - rc.top };
350 | this._img.style.opacity = DragDropTouch._OPACITY.toString();
351 | }
352 | // add image to document
353 | this._moveImage(e);
354 | document.body.appendChild(this._img);
355 | };
356 | // dispose of drag image element
357 | DragDropTouch.prototype._destroyImage = function () {
358 | if (this._img && this._img.parentElement) {
359 | this._img.parentElement.removeChild(this._img);
360 | }
361 | this._img = null;
362 | this._imgCustom = null;
363 | };
364 | // move the drag image element
365 | DragDropTouch.prototype._moveImage = function (e) {
366 | var _this = this;
367 | requestAnimationFrame(function () {
368 | if (_this._img) {
369 | var pt = _this._getPoint(e, true), s = _this._img.style;
370 | s.position = 'absolute';
371 | s.pointerEvents = 'none';
372 | s.zIndex = '999999';
373 | s.left = Math.round(pt.x - _this._imgOffset.x) + 'px';
374 | s.top = Math.round(pt.y - _this._imgOffset.y) + 'px';
375 | }
376 | });
377 | };
378 | // copy properties from an object to another
379 | DragDropTouch.prototype._copyProps = function (dst, src, props) {
380 | for (var i = 0; i < props.length; i++) {
381 | var p = props[i];
382 | dst[p] = src[p];
383 | }
384 | };
385 | DragDropTouch.prototype._copyStyle = function (src, dst) {
386 | // remove potentially troublesome attributes
387 | DragDropTouch._rmvAtts.forEach(function (att) {
388 | dst.removeAttribute(att);
389 | });
390 | // copy canvas content
391 | if (src instanceof HTMLCanvasElement) {
392 | var cSrc = src, cDst = dst;
393 | cDst.width = cSrc.width;
394 | cDst.height = cSrc.height;
395 | cDst.getContext('2d').drawImage(cSrc, 0, 0);
396 | }
397 | // copy style (without transitions)
398 | var cs = getComputedStyle(src);
399 | for (var i = 0; i < cs.length; i++) {
400 | var key = cs[i];
401 | if (key.indexOf('transition') < 0) {
402 | dst.style[key] = cs[key];
403 | }
404 | }
405 | dst.style.pointerEvents = 'none';
406 | // and repeat for all children
407 | for (var i = 0; i < src.children.length; i++) {
408 | this._copyStyle(src.children[i], dst.children[i]);
409 | }
410 | };
411 | DragDropTouch.prototype._dispatchEvent = function (e, type, target) {
412 | if (e && target) {
413 | var evt = document.createEvent('Event'), t = e.touches ? e.touches[0] : e;
414 | evt.initEvent(type, true, true);
415 | evt.button = 0;
416 | evt.which = evt.buttons = 1;
417 | this._copyProps(evt, e, DragDropTouch._kbdProps);
418 | this._copyProps(evt, t, DragDropTouch._ptProps);
419 | evt.dataTransfer = this._dataTransfer;
420 | target.dispatchEvent(evt);
421 | return evt.defaultPrevented;
422 | }
423 | return false;
424 | };
425 | // gets an element's closest draggable ancestor
426 | DragDropTouch.prototype._closestDraggable = function (e) {
427 | for (; e; e = e.parentElement) {
428 | if (e.hasAttribute('draggable') && e.draggable) {
429 | return e;
430 | }
431 | }
432 | return null;
433 | };
434 | return DragDropTouch;
435 | }());
436 | /*private*/ DragDropTouch._instance = new DragDropTouch(); // singleton
437 | // constants
438 | DragDropTouch._THRESHOLD = 5; // pixels to move before drag starts
439 | DragDropTouch._OPACITY = 0.5; // drag image opacity
440 | DragDropTouch._DBLCLICK = 500; // max ms between clicks in a double click
441 | DragDropTouch._CTXMENU = 900; // ms to hold before raising 'contextmenu' event
442 | DragDropTouch._ISPRESSHOLDMODE = false; // decides of press & hold mode presence
443 | DragDropTouch._PRESSHOLDAWAIT = 400; // ms to wait before press & hold is detected
444 | DragDropTouch._PRESSHOLDMARGIN = 25; // pixels that finger might shiver while pressing
445 | DragDropTouch._PRESSHOLDTHRESHOLD = 0; // pixels to move before drag starts
446 | // copy styles/attributes from drag source to drag image element
447 | DragDropTouch._rmvAtts = 'id,class,style,draggable'.split(',');
448 | // synthesize and dispatch an event
449 | // returns true if the event has been handled (e.preventDefault == true)
450 | DragDropTouch._kbdProps = 'altKey,ctrlKey,metaKey,shiftKey'.split(',');
451 | DragDropTouch._ptProps = 'pageX,pageY,clientX,clientY,screenX,screenY,offsetX,offsetY'.split(',');
452 | DragDropTouch_1.DragDropTouch = DragDropTouch;
453 | })(DragDropTouch || (DragDropTouch = {}));
454 |
--------------------------------------------------------------------------------
/src/ReactDragColumnView.jsx:
--------------------------------------------------------------------------------
1 | import ReactDragListView from './ReactDragListView';
2 |
3 | const UNIT_PX = 'px';
4 | const DRAG_LIND_STYLE = 'width:0;margin-left:-1px;margin-top:0;' +
5 | 'border-bottom:0 none;border-left:dashed 2px red;';
6 | const DIRECTIONS = {
7 | RIGHT: 2,
8 | LEFT: 4
9 | };
10 |
11 | class ReactDragColumnView extends ReactDragListView {
12 | getDragLine() {
13 | if (!this.dragLine) {
14 | super.getDragLine();
15 | this.dragLine.setAttribute('style', this.dragLine.getAttribute('style') + DRAG_LIND_STYLE);
16 | }
17 | return this.dragLine;
18 | }
19 |
20 | resolveAutoScroll(e, target) {
21 | if (!this.scrollElement) {
22 | return;
23 | }
24 | const { left, width } = this.scrollElement.getBoundingClientRect();
25 | const targetWidth = target.offsetWidth;
26 | const { pageX } = e;
27 | const compatibleWidth = (targetWidth * 2) / 3;
28 | this.direction = 0;
29 | if (pageX > ((left + width) - compatibleWidth)) {
30 | this.direction = DIRECTIONS.RIGHT;
31 | } else if (pageX < (left + compatibleWidth)) {
32 | this.direction = DIRECTIONS.LEFT;
33 | }
34 | if (this.direction) {
35 | if (this.scrollTimerId < 0) {
36 | this.scrollTimerId = setInterval(this.autoScroll, 20);
37 | }
38 | } else {
39 | this.stopAutoScroll();
40 | }
41 | }
42 |
43 | autoScroll() {
44 | const { scrollLeft } = this.scrollElement;
45 | if (this.direction === DIRECTIONS.RIGHT) {
46 | this.scrollElement.scrollLeft = scrollLeft + this.props.scrollSpeed;
47 | if (scrollLeft === this.scrollElement.scrollLeft) {
48 | this.stopAutoScroll();
49 | }
50 | } else if (this.direction === DIRECTIONS.LEFT) {
51 | this.scrollElement.scrollLeft = scrollLeft - this.props.scrollSpeed;
52 | if (this.scrollElement.scrollLeft <= 0) {
53 | this.stopAutoScroll();
54 | }
55 | } else {
56 | this.stopAutoScroll();
57 | }
58 | }
59 |
60 | fixDragLine(target) {
61 | const dragLine = this.getDragLine();
62 | if (!target || this.state.fromIndex < 0
63 | || this.state.fromIndex === this.state.toIndex) {
64 | this.hideDragLine();
65 | return;
66 | }
67 | const {
68 | left, top, width, height
69 | } = target.getBoundingClientRect();
70 | const lineLeft = (this.state.toIndex < this.state.fromIndex
71 | ? left
72 | : (left + width));
73 | if (this.props.enableScroll && this.scrollElement) {
74 | const {
75 | width: scrollWidth,
76 | left: scrollLeft
77 | } = this.scrollElement.getBoundingClientRect();
78 | if (lineLeft < (scrollLeft - 2) || lineLeft > (scrollLeft + scrollWidth + 2)) {
79 | this.hideDragLine();
80 | return;
81 | }
82 | }
83 | dragLine.style.top = top + UNIT_PX;
84 | dragLine.style.height = height + UNIT_PX;
85 | dragLine.style.left = lineLeft + UNIT_PX;
86 | dragLine.style.display = 'block';
87 | }
88 | }
89 |
90 | export default ReactDragColumnView;
91 |
--------------------------------------------------------------------------------
/src/ReactDragListView.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import { closest, getDomIndex, getScrollElement } from './util';
4 |
5 | const DEFAULT_NODE_SELECTOR = 'tr';
6 | const DIRECTIONS = {
7 | TOP: 1,
8 | BOTTOM: 3
9 | };
10 | const UNIT_PX = 'px';
11 | const DRAG_LIND_STYLE = 'position:fixed;z-index:9999;height:0;' +
12 | 'margin-top:-1px;border-bottom:dashed 2px red;display:none;';
13 |
14 | class ReactDragListView extends Component {
15 | static propTypes = {
16 | onDragEnd: PropTypes.func.isRequired,
17 | handleSelector: PropTypes.string,
18 | nodeSelector: PropTypes.string,
19 | ignoreSelector: PropTypes.string,
20 | enableScroll: PropTypes.bool,
21 | scrollSpeed: PropTypes.number,
22 | lineClassName: PropTypes.string,
23 | children: PropTypes.node
24 | }
25 |
26 | static defaultProps = {
27 | nodeSelector: DEFAULT_NODE_SELECTOR,
28 | ignoreSelector: '',
29 | enableScroll: true,
30 | scrollSpeed: 10,
31 | handleSelector: '',
32 | lineClassName: '',
33 | children: null
34 | }
35 |
36 | constructor(props) {
37 | super(props);
38 | this.onMouseDown = this.onMouseDown.bind(this);
39 | this.onDragStart = this.onDragStart.bind(this);
40 | this.onDragEnter = this.onDragEnter.bind(this);
41 | this.onDragEnd = this.onDragEnd.bind(this);
42 | this.autoScroll = this.autoScroll.bind(this);
43 |
44 | this.state = {
45 | fromIndex: -1,
46 | toIndex: -1
47 | };
48 |
49 | this.scrollElement = null;
50 | this.scrollTimerId = -1;
51 | this.direction = DIRECTIONS.BOTTOM;
52 | }
53 |
54 | componentWillUnmount() {
55 | if (this.dragLine && this.dragLine.parentNode) {
56 | this.dragLine.parentNode.removeChild(this.dragLine);
57 | this.dragLine = null;
58 | this.cacheDragTarget = null;
59 | }
60 | }
61 |
62 | onMouseDown(e) {
63 | this.startDrag(e);
64 | }
65 |
66 | onDragStart(e) {
67 | const target = this.getDragNode(e.target);
68 | const eventData = e;
69 | if (target) {
70 | const { parentNode } = target;
71 | eventData.dataTransfer.setData('Text', '');
72 | eventData.dataTransfer.effectAllowed = 'move';
73 | parentNode.ondragenter = this.onDragEnter;
74 | parentNode.ondragover = function(ev) {
75 | ev.preventDefault();
76 | return true;
77 | };
78 | const fromIndex = getDomIndex(target, this.props.ignoreSelector);
79 | this.setState({ fromIndex, toIndex: fromIndex });
80 | this.scrollElement = getScrollElement(parentNode);
81 | }
82 | }
83 |
84 | onDragEnter(e) {
85 | const target = this.getDragNode(e.target);
86 | const eventData = e;
87 | let toIndex;
88 | if (target) {
89 | toIndex = getDomIndex(target, this.props.ignoreSelector);
90 | if (this.props.enableScroll) {
91 | this.resolveAutoScroll(eventData, target);
92 | }
93 | } else {
94 | toIndex = -1;
95 | this.stopAutoScroll();
96 | }
97 | this.cacheDragTarget = target;
98 | this.setState({ toIndex });
99 | this.fixDragLine(target);
100 | }
101 |
102 | onDragEnd(e) {
103 | const target = this.getDragNode(e.target);
104 | this.stopAutoScroll();
105 | if (target) {
106 | target.removeAttribute('draggable');
107 | target.ondragstart = null;
108 | target.ondragend = null;
109 | target.parentNode.ondragenter = null;
110 | target.parentNode.ondragover = null;
111 | if (this.state.fromIndex >= 0 && this.state.fromIndex !== this.state.toIndex) {
112 | this.props.onDragEnd(this.state.fromIndex, this.state.toIndex);
113 | }
114 | }
115 | this.hideDragLine();
116 | this.setState({ fromIndex: -1, toIndex: -1 });
117 | }
118 |
119 | getDragNode(target) {
120 | return closest(target, this.props.nodeSelector, this.dragList);
121 | }
122 |
123 | getHandleNode(target) {
124 | return closest(
125 | target,
126 | this.props.handleSelector || this.props.nodeSelector,
127 | this.dragList
128 | );
129 | }
130 |
131 | getDragLine() {
132 | if (!this.dragLine) {
133 | this.dragLine = window.document.createElement('div');
134 | this.dragLine.setAttribute('style', DRAG_LIND_STYLE);
135 | window.document.body.appendChild(this.dragLine);
136 | }
137 | this.dragLine.className = this.props.lineClassName || '';
138 | return this.dragLine;
139 | }
140 |
141 | startDrag(e) {
142 | const handle = this.getHandleNode(e.target);
143 | if (handle) {
144 | const target = (!this.props.handleSelector || this.props.handleSelector
145 | === this.props.nodeSelector)
146 | ? handle
147 | : this.getDragNode(handle);
148 | if (target) {
149 | handle.setAttribute('draggable', false);
150 | target.setAttribute('draggable', true);
151 | target.ondragstart = this.onDragStart;
152 | target.ondragend = this.onDragEnd;
153 | }
154 | }
155 | }
156 |
157 | resolveAutoScroll(e, target) {
158 | if (!this.scrollElement) {
159 | return;
160 | }
161 | const { top, height } = this.scrollElement.getBoundingClientRect();
162 | const targetHeight = target.offsetHeight;
163 | const { pageY } = e;
164 | const compatibleHeight = targetHeight * (2 / 3);
165 | this.direction = 0;
166 | if (pageY > ((top + height) - compatibleHeight)) {
167 | this.direction = DIRECTIONS.BOTTOM;
168 | } else if (pageY < (top + compatibleHeight)) {
169 | this.direction = DIRECTIONS.TOP;
170 | }
171 | if (this.direction) {
172 | if (this.scrollTimerId < 0) {
173 | this.scrollTimerId = setInterval(this.autoScroll, 20);
174 | }
175 | } else {
176 | this.stopAutoScroll();
177 | }
178 | }
179 |
180 | stopAutoScroll() {
181 | clearInterval(this.scrollTimerId);
182 | this.scrollTimerId = -1;
183 | this.fixDragLine(this.cacheDragTarget);
184 | }
185 |
186 | autoScroll() {
187 | const { scrollTop } = this.scrollElement;
188 | if (this.direction === DIRECTIONS.BOTTOM) {
189 | this.scrollElement.scrollTop = scrollTop + this.props.scrollSpeed;
190 | if (scrollTop === this.scrollElement.scrollTop) {
191 | this.stopAutoScroll();
192 | }
193 | } else if (this.direction === DIRECTIONS.TOP) {
194 | this.scrollElement.scrollTop = scrollTop - this.props.scrollSpeed;
195 | if (this.scrollElement.scrollTop <= 0) {
196 | this.stopAutoScroll();
197 | }
198 | } else {
199 | this.stopAutoScroll();
200 | }
201 | }
202 |
203 | hideDragLine() {
204 | if (this.dragLine) {
205 | this.dragLine.style.display = 'none';
206 | }
207 | }
208 |
209 | fixDragLine(target) {
210 | const dragLine = this.getDragLine();
211 | if (!target || this.state.fromIndex < 0
212 | || this.state.fromIndex === this.state.toIndex) {
213 | this.hideDragLine();
214 | return;
215 | }
216 | const {
217 | left, top, width, height
218 | } = target.getBoundingClientRect();
219 | const lineTop = (this.state.toIndex < this.state.fromIndex
220 | ? top
221 | : (top + height));
222 | if (this.props.enableScroll && this.scrollElement) {
223 | const {
224 | height: scrollHeight,
225 | top: scrollTop
226 | } = this.scrollElement.getBoundingClientRect();
227 | if (lineTop < (scrollTop - 2) || lineTop > (scrollTop + scrollHeight + 2)) {
228 | this.hideDragLine();
229 | return;
230 | }
231 | }
232 | dragLine.style.left = left + UNIT_PX;
233 | dragLine.style.width = width + UNIT_PX;
234 | dragLine.style.top = lineTop + UNIT_PX;
235 | dragLine.style.display = 'block';
236 | }
237 |
238 | render() {
239 | return (
240 | // eslint-disable-next-line react/no-unknown-property
241 | { this.dragList = c; }}>
242 | {this.props.children}
243 |
244 | );
245 | }
246 | }
247 |
248 | export default ReactDragListView;
249 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import ReactDragListView from './ReactDragListView';
2 | import ReactDragColumnView from './ReactDragColumnView';
3 |
4 | ReactDragListView.DragColumn = ReactDragColumnView;
5 | export default ReactDragListView;
6 |
7 | // build node version: 11.15.0
8 |
--------------------------------------------------------------------------------
/src/util.js:
--------------------------------------------------------------------------------
1 | /* global Element */
2 |
3 | if (typeof Element !== 'undefined' && !Element.prototype.matches) {
4 | var proto = Element.prototype;
5 | proto.matches = proto.matchesSelector ||
6 | proto.mozMatchesSelector || proto.msMatchesSelector ||
7 | proto.oMatchesSelector || proto.webkitMatchesSelector;
8 | }
9 |
10 | const closest = function(el, selector, rootNode) {
11 | let element = el;
12 | while (element) {
13 | const isRoot = element === rootNode || element === document.body;
14 | if (isRoot || (element.nodeType === 1 && element.matches(selector))) {
15 | if (isRoot) {
16 | element = null;
17 | }
18 | break;
19 | }
20 | element = element.parentNode;
21 | }
22 | return element;
23 | };
24 |
25 | const getScrollElement = function(el) {
26 | let element = el;
27 | do {
28 | const { overflow } = window.getComputedStyle(element);
29 | if ((overflow === 'auto' || overflow === 'scroll')
30 | && (element && element.nodeType
31 | && (element.offsetWidth < element.scrollWidth
32 | || element.offsetHeight < element.scrollHeight))) {
33 | break;
34 | }
35 | if (!element || !element.nodeType || element === document.body) {
36 | element = null;
37 | break;
38 | }
39 | element = element.parentNode;
40 | } while (element);
41 | return element;
42 | };
43 |
44 | const getDomIndex = function(el, ignoreSelectors) {
45 | return Array.from(el.parentNode.children)
46 | .filter(e => (ignoreSelectors === '' ? true : !e.matches(ignoreSelectors)))
47 | .indexOf(el);
48 | };
49 |
50 | // const isTouchScreen = function() {
51 | // return window.navigator.maxTouchPoints > 0;
52 | // };
53 |
54 | export { getScrollElement, closest, getDomIndex };
55 |
--------------------------------------------------------------------------------