-
Notifications
You must be signed in to change notification settings - Fork 89
/
Copy pathSortableComposition.js
102 lines (80 loc) · 2.88 KB
/
SortableComposition.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import React from 'react'
import PropTypes from 'prop-types'
import { swapArrayElements, isMouseBeyond } from './helpers.js'
export const VERTICAL = 'VERTICAL'
export const HORIZONTAL = 'HORIZONTAL'
/*** Higher-order component - this component works like a factory for draggable items */
let draggingIndex = null
export function SortableComposition(Component, flowDirection = VERTICAL) {
return class Sortable extends React.Component {
sortEnd = (e) => {
e.preventDefault()
draggingIndex = null
}
sortStart = (e) => {
draggingIndex = e.currentTarget.dataset.id
let dt = e.dataTransfer
if (dt !== undefined) {
e.dataTransfer.setData('text', e.target.innerHTML)
//fix http://stackoverflow.com/questions/27656183/preserve-appearance-of-dragged-a-element-when-using-html5-draggable-attribute
if (dt.setDragImage && e.currentTarget.tagName.toLowerCase() === 'a') {
dt.setDragImage(e.target, 0, 0)
}
}
}
dragOver = (e) => {
e.preventDefault();
const { moveInMiddle, sortId } = this.props
const overEl = e.currentTarget //underlying element
const indexDragged = Number(overEl.dataset.id) //index of underlying element in the set DOM elements
const indexFrom = Number(draggingIndex)
const height = overEl.getBoundingClientRect().height
const width = overEl.getBoundingClientRect().width
const positionX = e.clientX
const positionY = e.clientY
const topOffset = overEl.getBoundingClientRect().top
const leftOffset = overEl.getBoundingClientRect().left
let mouseBeyond
let { items } = this.props
if (flowDirection === VERTICAL) {
mouseBeyond = isMouseBeyond(positionY, topOffset, height, moveInMiddle)
}
if (flowDirection === HORIZONTAL) {
mouseBeyond = isMouseBeyond(positionX, leftOffset, width, moveInMiddle)
}
let shouldSwapItems = isMouseBeyond && indexDragged !== indexFrom
if (indexDragged !== indexFrom && mouseBeyond) {
items = swapArrayElements(items, indexFrom, indexDragged)
draggingIndex = indexDragged
this.props.onSortItems(items)
}
}
render() {
let newProps = Object.assign({}, this.props)
delete newProps.onSortItems
const { sortId, ...props } = newProps
return (
<Component
draggable={true}
onDragOver={this.dragOver}
onDragStart={this.sortStart}
onDragEnd={this.sortEnd}
onTouchStart={this.sortStart}
onTouchMove={this.dragOver}
onTouchEnd={this.sortEnd}
data-id={sortId}
{...props}
/>
)
}
}
Sortable.propTypes = {
items: PropTypes.array.isRequired,
onSortItems: PropTypes.func.isRequired,
sortId: PropTypes.number,
};
Sortable.defaultProps = {
moveInMiddle: false
};
return Sortable
}