From 9c7a6765b91f4ccb701f4d25e5680748255ad624 Mon Sep 17 00:00:00 2001 From: Darkness <616487222@qq.com> Date: Mon, 10 Dec 2018 10:30:53 +0800 Subject: [PATCH 1/2] add: multi group --- .gitignore | 1 + react-sortable-hoc/Manager.js | 77 ++ react-sortable-hoc/SortableContainer/index.js | 969 ++++++++++++++++++ react-sortable-hoc/SortableElement/index.js | 94 ++ react-sortable-hoc/SortableGroup/index.js | 100 ++ react-sortable-hoc/SortableHandle/index.js | 31 + react-sortable-hoc/index.js | 10 + react-sortable-hoc/utils.js | 163 +++ 8 files changed, 1445 insertions(+) create mode 100755 react-sortable-hoc/Manager.js create mode 100755 react-sortable-hoc/SortableContainer/index.js create mode 100755 react-sortable-hoc/SortableElement/index.js create mode 100644 react-sortable-hoc/SortableGroup/index.js create mode 100755 react-sortable-hoc/SortableHandle/index.js create mode 100755 react-sortable-hoc/index.js create mode 100755 react-sortable-hoc/utils.js diff --git a/.gitignore b/.gitignore index d88833ad6..93503f3c3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ styles.min.css styles.min.css.map coverage npm-debug.log +.idea diff --git a/react-sortable-hoc/Manager.js b/react-sortable-hoc/Manager.js new file mode 100755 index 000000000..ff1ffb0c2 --- /dev/null +++ b/react-sortable-hoc/Manager.js @@ -0,0 +1,77 @@ +import { get } from 'lodash-es'; +export default class Manager { + refs = {}; + group = {}; + + add(collection, ref, index) { + if (!this.refs[collection]) { + this.refs[collection] = []; + } + if (this.start != null && this.start.index === index) { + this.start.node = ref.node; + } + + this.refs[collection].push(ref); + } + + remove(collection, ref) { + const index = this.getIndex(collection, ref); + + if (index !== -1) { + this.refs[collection].splice(index, 1); + } + } + + setGroup = (group, node) => { + this.group[group] = node; + } + + + refreshAllGroup = () => { + const groupKeys = Object.keys(this.group); + groupKeys.forEach((groupKey) => { + this.group[groupKey].sortGroupInfo.reRenderer + && this.group[groupKey].sortGroupInfo.reRenderer(); + }); + } + + getIsGroupDisabled = (collection) => { + const startManager = get(this, ['start'], null); + const startCollection = get(startManager, ['collection'], null); + const sortGroup = get(this, ['group', collection, 'sortGroupInfo', 'sortGroup'], {}); + if (startCollection != null + && startCollection !== collection + && !sortGroup[startCollection] + ) { + return true; + } + return false; + } + + isActive() { + return this.active; + } + + getActive() { + return this.refs[this.active.collection].find( + // eslint-disable-next-line eqeqeq + ({node}) => node.sortableInfo.index == this.active.index + ); + } + + getIndex(collection, ref) { + return this.refs[collection].indexOf(ref); + } + + getOrderedRefs(collection = this.active.collection) { + const orederedRefs = get(this.refs, [collection], []); + return orederedRefs.sort(sortByIndex); + } +} + +function sortByIndex( + {node: {sortableInfo: {index: index1}}}, + {node: {sortableInfo: {index: index2}}} +) { + return (index1 - index2); +} diff --git a/react-sortable-hoc/SortableContainer/index.js b/react-sortable-hoc/SortableContainer/index.js new file mode 100755 index 000000000..b52007810 --- /dev/null +++ b/react-sortable-hoc/SortableContainer/index.js @@ -0,0 +1,969 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {findDOMNode} from 'react-dom'; +import invariant from 'invariant'; + +import Manager from '../Manager'; +import sortManager from '../../sort_manager'; +import { + closest, + events, + vendorPrefix, + limit, + getEdgeOffset, + getElementMargin, + getLockPixelOffset, + getPosition, + isTouchEvent, + provideDisplayName, + omit, +} from '../utils'; + +// Export Higher Order Sortable Container Component +export default function sortableContainer(WrappedComponent, config = {withRef: false}) { + return class extends Component { + constructor(props) { + super(props); + this.manager = new Manager(); + this.events = { + start: this.handleStart, + move: this.handleMove, + end: this.handleEnd, + }; + + invariant( + !(props.distance && props.pressDelay), + 'Attempted to set both `pressDelay` and `distance` on SortableContainer, you may only use one or the other, not both at the same time.' + ); + + this.state = {}; + } + + static displayName = provideDisplayName('sortableList', WrappedComponent); + + static defaultProps = { + axis: () => { + return { + axis: 'y', + lockAxis: null, + } + }, + transitionDuration: 300, + pressDelay: 0, + pressThreshold: 5, + distance: 0, + hideSortableGhost: true, + shouldCancelStart: function(e) { + // Cancel sorting if the event target is an `input`, `textarea`, `select` or `option` + const disabledElements = ['input', 'textarea', 'select', 'option', 'button']; + + if (disabledElements.indexOf(e.target.tagName.toLowerCase()) !== -1) { + return true; // Return true to cancel sorting + } + }, + lockToContainerEdges: false, + lockOffset: '50%', + getHelperDimensions: ({node}) => ({ + width: node.offsetWidth, + height: node.offsetHeight, + }), + }; + + static propTypes = { + axis: PropTypes.func, + distance: PropTypes.number, + lockAxis: PropTypes.string, + helperClass: PropTypes.string, + transitionDuration: PropTypes.number, + onSortStart: PropTypes.func, + onSortMove: PropTypes.func, + onSortOver: PropTypes.func, + onSortEnd: PropTypes.func, + shouldCancelStart: PropTypes.func, + pressDelay: PropTypes.number, + useDragHandle: PropTypes.bool, + hideSortableGhost: PropTypes.bool, + lockToContainerEdges: PropTypes.bool, + lockOffset: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.number, PropTypes.string]) + ), + ]), + getContainer: PropTypes.func, + getHelperDimensions: PropTypes.func, + }; + + static childContextTypes = { + manager: PropTypes.object.isRequired, + }; + + getChildContext() { + return { + manager: this.manager, + }; + } + + componentDidMount() { + + /* + * Set our own default rather than using defaultProps because Jest + * snapshots will serialize window, causing a RangeError + * https://github.com/clauderic/react-sortable-hoc/issues/249 + */ + + const container = this.getContainer(); + + Promise.resolve(container).then((containerNode) => { + this.container = containerNode; + this.document = this.container.ownerDocument || document; + const contentWindow = this.document.defaultView; + this.contentWindow = typeof contentWindow === 'function' + ? contentWindow() + : contentWindow; + + for (const key in this.events) { + if (this.events.hasOwnProperty(key)) { + events[key].forEach(eventName => + this.container.addEventListener(eventName, this.events[key], false) + ); + } + } + }); + } + + componentWillUnmount() { + if (this.container) { + for (const key in this.events) { + if (this.events.hasOwnProperty(key)) { + events[key].forEach(eventName => + this.container.removeEventListener(eventName, this.events[key]) + ); + } + } + } + } + + checkSortableInfo = el => el.sortableInfo != null; + checkSortableHandle = el => el.sortableHandle != null; + checkSortableGroup = el => el.sortGroupInfo != null; + + + handleStart = event => { + const {distance, shouldCancelStart, useDragHandle} = this.props; + + if (event.button === 2 || shouldCancelStart(event)) { + return false; + } + + this._touched = true; + this._pos = getPosition(event); + + const node = closest(event.target, this.checkSortableInfo); + const groupNode = closest(event.target, this.checkSortableGroup); + + if ( + node && + node.sortableInfo && + this.nodeIsChild(node) && + !this.state.sorting + ) { + const {index, collection} = node.sortableInfo; + const dimensions = this.props.getHelperDimensions({ index, collection, node }); + this.manager.active = {index, collection, node, groupNode}; + this.manager.start = {index, collection, node, dimensions }; + this.manager.refreshAllGroup(); + if ( + useDragHandle && !closest(event.target, this.checkSortableHandle) + ) + return; + + /* + * Fixes a bug in Firefox where the :active state of anchor tags + * prevent subsequent 'mousemove' events from being fired + * (see https://github.com/clauderic/react-sortable-hoc/issues/118) + */ + if (!isTouchEvent(event) && event.target.tagName.toLowerCase() === 'a') { + event.preventDefault(); + } + + if (!distance) { + if (this.props.pressDelay === 0) { + this.handlePress(event); + } else { + this.pressTimer = setTimeout( + () => this.handlePress(event), + this.props.pressDelay + ); + } + } + } + }; + + nodeIsChild = node => { + return node.sortableInfo.manager === this.manager; + }; + + clearNodeTransform = (nodes) => { + if (!nodes) return; + for (let i = 0, len = nodes.length; i < len; i += 1) { + const node = nodes[i]; + const el = node.node; + + // Clear the cached offsetTop / offsetLeft value + node.edgeOffset = null; + + // Remove the transforms / transitions + el.style[`${vendorPrefix}Transform`] = ''; + el.style[`${vendorPrefix}TransitionDuration`] = ''; + } + } + handleMove = event => { + const {distance, pressThreshold} = this.props; + + const sortableGroup = closest(event.target, this.checkSortableGroup); + if (sortableGroup != null && + this.manager.active != null && + sortableGroup.sortGroupInfo.sortGroup[this.manager.start.collection] && + sortableGroup.sortGroupInfo.collection !== this.manager.active.collection) { + const newCollection = sortableGroup.sortGroupInfo.collection; + const oldCollection = this.manager.active.collection; + const oldNodes = this.manager.refs[oldCollection]; + this.clearNodeTransform(oldNodes); + let nextIndex = this.manager.start.index; + if (newCollection !== this.manager.start.collection) { + nextIndex = sortableGroup.sortGroupInfo.collectionCount; + } + this.offsetEdge.top -= this.lastScrollTop; + this.offsetEdge.left -= this.lastScrollLeft; + this.changeTranslate(sortableGroup); + this.initScroll(sortableGroup); + this.offsetEdge.top += this.scrollContainer.scrollTop; + this.offsetEdge.left += this.scrollContainer.scrollLeft; + + this.manager.active = { + index: nextIndex, + collection: newCollection, + node: this.manager.start.node, + groupNode: sortableGroup, + }; + this.manager.group[oldCollection].sortGroupInfo.reRenderer(); + this.manager.group[newCollection].sortGroupInfo.reRenderer(); + this.index = nextIndex; + this.newIndex = null; + this.handleInitNewCollectionStatus(); + } + + if (!this.state.sorting && this._touched) { + const position = getPosition(event); + const delta = this._delta = { + x: this._pos.x - position.x, + y: this._pos.y - position.y, + }; + const combinedDelta = Math.abs(delta.x) + Math.abs(delta.y); + + if (!distance && (!pressThreshold || pressThreshold && combinedDelta >= pressThreshold)) { + clearTimeout(this.cancelTimer); + this.cancelTimer = setTimeout(this.cancel, 0); + } else if (distance && combinedDelta >= distance && this.manager.isActive()) { + this.handlePress(event); + } + } + }; + + handleEnd = () => { + this._touched = false; + this.cancel(); + }; + + cancel = () => { + const {distance} = this.props; + const {sorting} = this.state; + + if (!sorting) { + if (!distance) { + clearTimeout(this.pressTimer); + } + this.manager.active = null; + } + }; + + lastScrollTop = 0; + lastScrollLeft = 0; + initTranslate = (node, container, isOutContainer = false) => { + const containerBoundingRect = container.getBoundingClientRect(); + const nodeBoundingRect = node.getBoundingClientRect(); + const minTranslate = {}; + const maxTranslate = {}; + let axis = this.axis; + if (isOutContainer) { + axis = this.containerAxis; + } + if (axis.x) { + minTranslate.x = containerBoundingRect.left - + nodeBoundingRect.left - + this.width / 2; + maxTranslate.x = containerBoundingRect.left + containerBoundingRect.width - + nodeBoundingRect.left - + this.width / 2; + } + if (axis.y) { + minTranslate.y = containerBoundingRect.top - + nodeBoundingRect.top - + this.height / 2; + maxTranslate.y = containerBoundingRect.top + containerBoundingRect.height - + nodeBoundingRect.top - + this.height / 2; + } + if (isOutContainer) { + this.outContainer = {}; + this.outContainer.minTranslate = minTranslate; + this.outContainer.maxTranslate = maxTranslate; + } else { + this.scrollContainer = container; + this.lastScrollTop = this.scrollContainer.scrollTop; + this.lastScrollLeft = this.scrollContainer.lastScrollLeft; + this.minTranslate = minTranslate; + this.maxTranslate = maxTranslate; + this.boundingClientRect = nodeBoundingRect; + this.containerBoundingRect = containerBoundingRect; + this.initTranslate(node, this.container, true) + } + } + + changeTranslate = (container) => { + this.scrollContainer = container; + const containerBoundingRect = container.getBoundingClientRect(); + if (this.axis.x) { + const oldTranslateMinx = this.minTranslate.x; + const oldContainerWidth = this.containerBoundingRect.width; + const oldContainerLeft = this.containerBoundingRect.left; + const newContainerWidth = containerBoundingRect.width; + const newContainerLeft = containerBoundingRect.left; + const oldTranslateMaxx = this.maxTranslate.x; + this.minTranslate.x = oldTranslateMinx - (newContainerLeft - oldContainerLeft); + this.maxTranslate.x = oldTranslateMaxx + ( + newContainerWidth + newContainerLeft - oldContainerWidth - oldContainerLeft + ) + } + if (this.axis.y) { + const oldTranslateMiny = this.minTranslate.y; + const oldContainerHeight = this.containerBoundingRect.height; + const oldContainerTop = this.containerBoundingRect.top; + const newContainerHeight = containerBoundingRect.height; + const newContainerTop = containerBoundingRect.top; + + const oldTranslateMaxy = this.maxTranslate.y; + + this.minTranslate.y = oldTranslateMiny - (newContainerTop - oldContainerTop); + this.maxTranslate.y = oldTranslateMaxy + ( + newContainerHeight + newContainerTop - oldContainerHeight - oldContainerTop + ) + } + + this.containerBoundingRect = containerBoundingRect; + + this.lastScrollTop = this.scrollContainer.scrollTop; + this.lastScrollLeft = this.scrollContainer.lastScrollLeft; + } + + + + + initScroll = (container) => { + this.initialScroll = { + top: container.scrollTop, + left: container.scrollLeft, + }; + } + handlePress = event => { + const active = this.manager.getActive(); + + if (active) { + const { + getHelperDimensions, + helperClass, + hideSortableGhost, + onSortStart, + } = this.props; + const {node} = active; + // this.sortableGhost = node; + const {index, collection} = node.sortableInfo; + const margin = getElementMargin(node); + const groupNode = closest(node, this.checkSortableGroup); + const container = groupNode || this.container; + + + const dimensions = getHelperDimensions({index, node, collection}); + this.width = dimensions.width; + this.height = dimensions.height; + + const axis = this.props.axis(collection).axis; + this.axis = { + x: axis.indexOf('x') >= 0, + y: axis.indexOf('y') >= 0, + }; + const containerAxis = this.props.axis().axis; + this.containerAxis = { + x: containerAxis.indexOf('x') >= 0, + y: containerAxis.indexOf('y') >= 0, + }; + + this.initTranslate(node, container); + + this.lastTransform = { + x: 0, + y: 0, + } + + this.node = node; + this.margin = margin; + this.marginOffset = { + x: this.margin.left + this.margin.right, + y: Math.max(this.margin.top, this.margin.bottom), + }; + + this.index = index; + this.newIndex = index; + + this.offsetEdge = getEdgeOffset(node, this.container); + this.initialOffset = getPosition(event); + this.initScroll(container); + + this.initialWindowScroll = { + top: window.pageYOffset, + left: window.pageXOffset, + }; + + const fields = node.querySelectorAll('input, textarea, select'); + const clonedNode = node.cloneNode(true); + const clonedFields = [ + ...clonedNode.querySelectorAll('input, textarea, select'), + ]; // Convert NodeList to Array + + clonedFields.forEach((field, index) => { + if (field.type !== 'file' && fields[index]) { + field.value = fields[index].value; + } + }); + + this.helper = this.document.body.appendChild(clonedNode); + + this.helper.style.position = 'fixed'; + this.helper.style.top = `${this.boundingClientRect.top - margin.top}px`; + this.helper.style.left = `${this.boundingClientRect.left - margin.left}px`; + this.helper.style.width = `${this.width}px`; + this.helper.style.height = `${this.height}px`; + this.helper.style.boxSizing = 'border-box'; + this.helper.style.pointerEvents = 'none'; + + // if (hideSortableGhost) { + // this.sortableGhost = node; + // node.style.visibility = 'hidden'; + // node.style.opacity = 0; + // } + + + + if (helperClass) { + this.helper.classList.add(...helperClass.split(' ')); + } + + this.listenerNode = event.touches ? node : this.contentWindow; + events.move.forEach(eventName => + this.listenerNode.addEventListener( + eventName, + this.handleSortMove, + false + )); + events.end.forEach(eventName => + this.listenerNode.addEventListener( + eventName, + this.handleSortEnd, + false + )); + + this.setState({ + sorting: true, + sortingIndex: index, + }); + + if (onSortStart) { + onSortStart({node, index, collection}, event); + } + } + }; + + handleSortMove = event => { + const {onSortMove} = this.props; + event.preventDefault(); // Prevent scrolling on mobile + + this.updatePosition(event); + this.animateNodes(); + this.autoscroll(event); + + if (onSortMove) { + onSortMove(event); + } + }; + + handleInitNewCollectionStatus = () => { + clearInterval(this.autoscrollInterval); + this.autoscrollInterval = null; + this.isAutoScrolling = false; + } + + handleSortEnd = event => { + const {hideSortableGhost, onSortEnd} = this.props; + const {collection} = this.manager.active; + const oldCollection = this.manager.start.collection; + const oldIndex = this.manager.start.index; + + const startNode = this.manager.start.node; + startNode.style.visibility = ''; + startNode.style.opacity = ''; + + // Remove the event listeners if the node is still in the DOM + if (this.listenerNode) { + events.move.forEach(eventName => + this.listenerNode.removeEventListener( + eventName, + this.handleSortMove + )); + events.end.forEach(eventName => + this.listenerNode.removeEventListener(eventName, this.handleSortEnd)); + } + + // Remove the helper from the DOM + this.helper.parentNode.removeChild(this.helper); + + if (hideSortableGhost && this.sortableGhost) { + this.sortableGhost.style.visibility = ''; + this.sortableGhost.style.opacity = ''; + } + + const nodes = this.manager.refs[collection]; + this.clearNodeTransform(nodes); + const oldNodes = this.manager.refs[oldCollection]; + oldNodes.forEach((node) => { + node.node.style.visibility = ''; + node.node.style.opacity = ''; + }) + // this.clearNodeTransform(oldNodes); + // if (oldNodes[oldIndex] && oldNodes[oldIndex].node) { + // oldNodes[oldIndex].node.style.visibility = ''; + // oldNodes[oldIndex].node.style.opacity = ''; + // } + this.handleInitNewCollectionStatus(); + + // Update state + this.manager.active = null; + this.manager.start = null; + + this.setState({ + sorting: false, + sortingIndex: null, + }); + + this.manager.refreshAllGroup(); + + if (typeof onSortEnd === 'function') { + onSortEnd( + { + oldIndex: oldIndex, + newIndex: this.newIndex, + oldCollection, + newCollection: collection, + }, + event + ); + } + + this._touched = false; + }; + + getLockPixelOffsets() { + const {width, height} = this; + const {lockOffset} = this.props; + const offsets = Array.isArray(lockOffset) + ? lockOffset + : [lockOffset, lockOffset]; + + invariant( + offsets.length === 2, + 'lockOffset prop of SortableContainer should be a single ' + + 'value or an array of exactly two values. Given %s', + lockOffset + ); + + const [minLockOffset, maxLockOffset] = offsets; + + return [ + getLockPixelOffset({lockOffset: minLockOffset, width, height}), + getLockPixelOffset({lockOffset: maxLockOffset, width, height}), + ]; + } + + updatePosition(event) { + const {lockToContainerEdges} = this.props; + const lockAxis = this.props.axis(this.manager.start.collection).lockAxis; + const offset = getPosition(event); + const translate = { + x: offset.x - this.initialOffset.x, + y: offset.y - this.initialOffset.y, + }; + + // Adjust for window scroll + translate.y -= (window.pageYOffset - this.initialWindowScroll.top); + translate.x -= (window.pageXOffset - this.initialWindowScroll.left); + + this.translate = translate; + + if (lockToContainerEdges) { + const [minLockOffset, maxLockOffset] = this.getLockPixelOffsets(); + const minOffset = { + x: this.width / 2 - minLockOffset.x, + y: this.height / 2 - minLockOffset.y, + }; + const maxOffset = { + x: this.width / 2 - maxLockOffset.x, + y: this.height / 2 - maxLockOffset.y, + }; + + translate.x = limit( + this.minTranslate.x + minOffset.x, + this.maxTranslate.x - maxOffset.x, + translate.x + ); + translate.y = limit( + this.minTranslate.y + minOffset.y, + this.maxTranslate.y - maxOffset.y, + translate.y + ); + } + + if (lockAxis === 'x') { + translate.y = 0; + } else if (lockAxis === 'y') { + translate.x = 0; + } + + this.helper.style[ + `${vendorPrefix}Transform` + ] = `translate3d(${translate.x}px,${translate.y}px, 0)`; + } + + animateNodes() { + const {transitionDuration, hideSortableGhost, onSortOver} = this.props; + const nodes = this.manager.getOrderedRefs(); + const containerScrollDelta = { + left: this.scrollContainer.scrollLeft - this.initialScroll.left, + top: this.scrollContainer.scrollTop - this.initialScroll.top, + }; + const sortingOffset = { + left: this.offsetEdge.left + this.translate.x + containerScrollDelta.left, + top: this.offsetEdge.top + this.translate.y + containerScrollDelta.top, + }; + const windowScrollDelta = { + top: (window.pageYOffset - this.initialWindowScroll.top), + left: (window.pageXOffset - this.initialWindowScroll.left), + }; + const prevIndex = this.newIndex; + this.newIndex = null; + this.lastTransform = { + x: 0, + y: 0, + } + + for (let i = 0, len = nodes.length; i < len; i++) { + const {node} = nodes[i]; + const index = node.sortableInfo.index; + const width = node.offsetWidth; + const height = node.offsetHeight; + const offset = { + width: this.width > width ? width / 2 : this.width / 2, + height: this.height > height ? height / 2 : this.height / 2, + }; + + const translate = { + x: 0, + y: 0, + }; + let {edgeOffset} = nodes[i]; + + // If we haven't cached the node's offsetTop / offsetLeft value + if (!edgeOffset) { + nodes[i].edgeOffset = (edgeOffset = getEdgeOffset(node, this.container)); + } + + // Get a reference to the next and previous node + const nextNode = i < nodes.length - 1 && nodes[i + 1]; + const prevNode = i > 0 && nodes[i - 1]; + + // Also cache the next node's edge offset if needed. + // We need this for calculating the animation in a grid setup + if (nextNode && !nextNode.edgeOffset) { + nextNode.edgeOffset = getEdgeOffset(nextNode.node, this.container); + } + + // If the node is the one we're currently animating, skip it + const thisIndex = this.manager.active.index; + if (index === thisIndex) { + this.sortableGhost = node; + if (hideSortableGhost) { + /* + * With windowing libraries such as `react-virtualized`, the sortableGhost + * node may change while scrolling down and then back up (or vice-versa), + * so we need to update the reference to the new node just to be safe. + */ + node.style.visibility = 'hidden'; + node.style.opacity = 0; + } + continue; + } + + if (transitionDuration) { + node.style[ + `${vendorPrefix}TransitionDuration` + ] = `${transitionDuration}ms`; + } + + if (this.axis.x) { + if (this.axis.y) { + // Calculations for a grid setup + if ( + index < thisIndex && + ( + ((sortingOffset.left + windowScrollDelta.left) - offset.width <= edgeOffset.left && + (sortingOffset.top + windowScrollDelta.top) <= edgeOffset.top + offset.height) || + (sortingOffset.top + windowScrollDelta.top) + offset.height <= edgeOffset.top + ) + ) { + // If the current node is to the left on the same row, or above the node that's being dragged + // then move it to the right + translate.x = this.width + this.marginOffset.x; + if ( + edgeOffset.left + translate.x > + this.containerBoundingRect.width - offset.width + ) { + // If it moves passed the right bounds, then animate it to the first position of the next row. + // We just use the offset of the next node to calculate where to move, because that node's original position + // is exactly where we want to go + translate.x = nextNode.edgeOffset.left - edgeOffset.left; + translate.y = nextNode.edgeOffset.top - edgeOffset.top; + } + if (this.newIndex === null) { + this.newIndex = index; + } + } else if ( + index > thisIndex && + ( + ((sortingOffset.left + windowScrollDelta.left) + offset.width >= edgeOffset.left && + (sortingOffset.top + windowScrollDelta.top) + offset.height >= edgeOffset.top) || + (sortingOffset.top + windowScrollDelta.top) + offset.height >= edgeOffset.top + height + ) + ) { + // If the current node is to the right on the same row, or below the node that's being dragged + // then move it to the left + translate.x = -(this.width + this.marginOffset.x); + if ( + edgeOffset.left + translate.x < + this.containerBoundingRect.left + offset.width + ) { + // If it moves passed the left bounds, then animate it to the last position of the previous row. + // We just use the offset of the previous node to calculate where to move, because that node's original position + // is exactly where we want to go + translate.x = prevNode.edgeOffset.left - edgeOffset.left; + translate.y = prevNode.edgeOffset.top - edgeOffset.top; + } + this.newIndex = index; + } + } else { + if ( + index > thisIndex && + (sortingOffset.left + windowScrollDelta.left) + offset.width >= edgeOffset.left + ) { + translate.x = -(this.width + this.marginOffset.x); + this.newIndex = index; + } else if ( + index < thisIndex && + (sortingOffset.left + windowScrollDelta.left) <= edgeOffset.left + offset.width + ) { + translate.x = this.width + this.marginOffset.x; + if (this.newIndex == null) { + this.newIndex = index; + } + } + } + } else if (this.axis.y) { + if ( + index > thisIndex && + (sortingOffset.top + windowScrollDelta.top) + offset.height >= edgeOffset.top + ) { + translate.y = -(this.height + this.marginOffset.y); + this.newIndex = index; + } else if ( + index < thisIndex && + (sortingOffset.top + windowScrollDelta.top) <= edgeOffset.top + offset.height + ) { + translate.y = this.height + this.marginOffset.y; + if (this.newIndex == null) { + this.newIndex = index; + } + } + } + // const nodeStyle = this.props.getHelperDimensions({ node }); + // if (translate.x > 0) { + // this.lastTransform.x -= nodeStyle.width; + // } + // if (translate.y > 0) { + // this.lastTransform.y -= nodeStyle.height; + // } + // if (translate.x < 0) { + // this.lastTransform.x += nodeStyle.width; + // } + // if (translate.y < 0) { + // this.lastTransform.y += nodeStyle.height; + // } + node.style[`${vendorPrefix}Transform`] = `translate3d(${translate.x}px,${translate.y}px,0)`; + } + // this.sortableGhost.style[`${vendorPrefix}Transform`] = `translate3d(${this.lastTransform.x}px,${this.lastTransform.y}px,0)`; + + if (this.newIndex == null) { + this.newIndex = this.index; + } + + if (onSortOver && this.newIndex !== prevIndex) { + onSortOver({ + newIndex: this.newIndex, + oldIndex: prevIndex, + index: this.index, + oldCollection: this.manager.start.collection, + collection: this.manager.active.collection, + }); + } + } + + autoscroll = ( + event, + scrollContainer = this.scrollContainer, + maxTranslate = this.maxTranslate, + minTranslate = this.minTranslate, + isOutContainer = false, + ) => { + const translate = this.translate; + const direction = { + x: 0, + y: 0, + }; + const speed = { + x: 1, + y: 1, + }; + const acceleration = { + x: 10, + y: 10, + }; + + if (translate.y >= maxTranslate.y - this.height / 2) { + direction.y = 1; // Scroll Down + speed.y = acceleration.y * Math.abs((maxTranslate.y - this.height / 2 - translate.y) / this.height); + } else if (translate.x >= maxTranslate.x - this.width / 2) { + direction.x = 1; // Scroll Right + speed.x = acceleration.x * Math.abs((maxTranslate.x - this.width / 2 - translate.x) / this.width); + } else if (translate.y <= minTranslate.y + this.height / 2) { + direction.y = -1; // Scroll Up + speed.y = acceleration.y * Math.abs((translate.y - this.height / 2 - minTranslate.y) / this.height); + } else if (translate.x <= minTranslate.x + this.width / 2) { + direction.x = -1; // Scroll Left + speed.x = acceleration.x * Math.abs((translate.x - this.width / 2 - minTranslate.x) / this.width); + } + + if (this.autoscrollInterval) { + clearInterval(this.autoscrollInterval); + this.autoscrollInterval = null; + this.isAutoScrolling = false; + } + + if (direction.x !== 0 || direction.y !== 0) { + this.autoscrollInterval = setInterval( + () => { + this.isAutoScrolling = true; + const offset = { + top: 1 * speed.y * direction.y, + left: 1 * speed.x * direction.x, + }; + // const scrollContainer = this.manager.active.groupNode || this.scrollContainer; + scrollContainer.scrollLeft += offset.left; + scrollContainer.scrollTop += offset.top; + this.translate.y += offset.top; + this.translate.x += offset.left; + this.animateNodes(); + }, + 5 + ); + } + // console.log('direction', direction.x, direction.y, !isOutContainer); + if (direction.x === 0 && direction.y === 0 && !isOutContainer) { + this.autoscroll( + event, + this.container, + this.outContainer.maxTranslate, + this.outContainer.minTranslate, + true + ) + } + }; + + getWrappedInstance() { + invariant( + config.withRef, + 'To access the wrapped instance, you need to pass in {withRef: true} as the second argument of the SortableContainer() call' + ); + + return this.refs.wrappedInstance; + } + + getContainer() { + const {getContainer} = this.props; + + if (typeof getContainer !== 'function') { + return findDOMNode(this); + } + + return getContainer(config.withRef ? this.getWrappedInstance() : undefined); + } + + render() { + const ref = config.withRef ? 'wrappedInstance' : null; + + return ( + + ); + } + }; +} + diff --git a/react-sortable-hoc/SortableElement/index.js b/react-sortable-hoc/SortableElement/index.js new file mode 100755 index 000000000..917fc25c2 --- /dev/null +++ b/react-sortable-hoc/SortableElement/index.js @@ -0,0 +1,94 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {findDOMNode} from 'react-dom'; +import invariant from 'invariant'; + +import {provideDisplayName, omit} from '../utils'; + +// Export Higher Order Sortable Element Component +export default function sortableElement(WrappedComponent, config = {withRef: false}) { + return class extends Component { + static displayName = provideDisplayName('sortableElement', WrappedComponent); + + static contextTypes = { + manager: PropTypes.object.isRequired, + }; + + static propTypes = { + index: PropTypes.number.isRequired, + collection: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + disabled: PropTypes.bool, + }; + + static defaultProps = { + collection: 0, + }; + + componentDidMount() { + const {collection, disabled, index} = this.props; + + if (!disabled) { + this.setDraggable(collection, index); + } + } + + componentWillReceiveProps(nextProps) { + if (this.props.index !== nextProps.index && this.node) { + this.node.sortableInfo.index = nextProps.index; + } + if (this.props.disabled !== nextProps.disabled) { + const {collection, disabled, index} = nextProps; + if (disabled) { + this.removeDraggable(collection); + } else { + this.setDraggable(collection, index); + } + } else if (this.props.collection !== nextProps.collection) { + this.removeDraggable(this.props.collection); + this.setDraggable(nextProps.collection, nextProps.index); + } + } + + componentWillUnmount() { + const {collection, disabled} = this.props; + + if (!disabled) this.removeDraggable(collection); + } + + setDraggable(collection, index) { + const node = (this.node = findDOMNode(this)); + + node.sortableInfo = { + index, + collection, + manager: this.context.manager, + }; + + this.ref = {node}; + this.context.manager.add(collection, this.ref, index); + } + + removeDraggable(collection) { + this.context.manager.remove(collection, this.ref); + } + + getWrappedInstance() { + invariant( + config.withRef, + 'To access the wrapped instance, you need to pass in {withRef: true} as the second argument of the SortableElement() call' + ); + return this.refs.wrappedInstance; + } + + render() { + const ref = config.withRef ? 'wrappedInstance' : null; + + return ( + + ); + } + }; +} diff --git a/react-sortable-hoc/SortableGroup/index.js b/react-sortable-hoc/SortableGroup/index.js new file mode 100644 index 000000000..17ec4143c --- /dev/null +++ b/react-sortable-hoc/SortableGroup/index.js @@ -0,0 +1,100 @@ +import { Component } from 'react'; +import PropTypes from 'prop-types'; +import { get, set } from 'lodash-es'; +import { findDOMNode } from 'react-dom'; + +export default class SortableGroup extends Component { + static contextTypes = { + manager: PropTypes.object.isRequired, + }; + + static propTypes = { + sortGroup: PropTypes.any, + collection: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + collectionCount: PropTypes.number, + defaultNodeHeight: PropTypes.number, + isDisabledChecker: PropTypes.func, + onReRenderer: PropTypes.func, + }; + + static defaultProps = { + collection: 0, + collectionCount: 0, + defaultNodeHeight: 75, + sortGroup: {}, + isDisabledChecker: () => { return true; }, + onReRenderer: () => {}, + }; + + state = { + activeCollection: null, + startCollection: null, + } + + componentDidMount() { + const { collection } = this.props; + this.setDragGroup(collection); + } + + componentWillReceiveProps(nextProps) { + if (nextProps.collectionCount !== this.props.collectionCount) { + this.refreshCollectionCount(nextProps.collectionCount); + } + } + + refreshCollectionCount = (count) => { + set(this.context, ['manager', 'group', this.props.collection, 'sortGroupInfo', 'collectionCount'], count); + } + + setDragGroup = (collection) => { + const node = (this.node = findDOMNode(this)); + node.sortGroupInfo = { + collection, + sortGroup: this.props.sortGroup, + collectionCount: this.props.collectionCount, + reRenderer: this.handleReRenderer, + }; + this.context.manager.setGroup(collection, node); + this.ref = { node }; + } + + handleReRenderer = () => { + const manager = this.context.manager; + const startManager = get(manager, ['start'], null); + const startCollection = get(startManager, ['collection'], null); + const activeManager = get(manager, ['active'], null); + const activeCollection = get(activeManager, ['collection'], null); + this.setState({ + activeCollection, + startCollection, + }); + this.props.onReRenderer(); + } + + render() { + let placeholderNode = null; + const manager = this.context.manager; + const startManager = get(manager, ['start'], null); + const startCollection = this.state.startCollection; + + const disabled = manager.getIsGroupDisabled(this.props.collection) + && this.props.isDisabledChecker(startCollection); + + const activeCollection = this.state.activeCollection; + if (activeCollection != null && activeCollection === this.props.collection + && startCollection !== this.props.collection + ) { + placeholderNode = get(startManager, ['node'], null); + } + const placeholderDimensions = get(startManager, 'dimensions', { + height: this.props.defaultNodeHeight, + width: 0, + }); + return this.props.children({ + placeholderNode, + placeholderDimensions, + disabled, + updateStamp: this.state.updateStamp, + }); + } +} diff --git a/react-sortable-hoc/SortableHandle/index.js b/react-sortable-hoc/SortableHandle/index.js new file mode 100755 index 000000000..4782999ab --- /dev/null +++ b/react-sortable-hoc/SortableHandle/index.js @@ -0,0 +1,31 @@ +import React, {Component} from 'react'; +import {findDOMNode} from 'react-dom'; +import invariant from 'invariant'; + +import {provideDisplayName} from '../utils'; + +// Export Higher Order Sortable Element Component +export default function sortableHandle(WrappedComponent, config = {withRef: false}) { + return class extends Component { + static displayName = provideDisplayName('sortableHandle', WrappedComponent); + + componentDidMount() { + const node = findDOMNode(this); + node.sortableHandle = true; + } + + getWrappedInstance() { + invariant( + config.withRef, + 'To access the wrapped instance, you need to pass in {withRef: true} as the second argument of the SortableHandle() call' + ); + return this.refs.wrappedInstance; + } + + render() { + const ref = config.withRef ? 'wrappedInstance' : null; + + return ; + } + }; +} diff --git a/react-sortable-hoc/index.js b/react-sortable-hoc/index.js new file mode 100755 index 000000000..8df6b570a --- /dev/null +++ b/react-sortable-hoc/index.js @@ -0,0 +1,10 @@ +export SortableContainer from './SortableContainer'; +export SortableElement from './SortableElement'; +export SortableHandle from './SortableHandle'; +export SortableGroup from './SortableGroup'; + +export sortableContainer from './SortableContainer'; +export sortableElement from './SortableElement'; +export sortableHandle from './SortableHandle'; + +export {arrayMove} from './utils'; diff --git a/react-sortable-hoc/utils.js b/react-sortable-hoc/utils.js new file mode 100755 index 000000000..113c72fe0 --- /dev/null +++ b/react-sortable-hoc/utils.js @@ -0,0 +1,163 @@ +import invariant from 'invariant'; +import memoize from 'memoizee'; + +export function arrayMove(arr, previousIndex, newIndex) { + const array = arr.slice(0); + if (newIndex >= array.length) { + let k = newIndex - array.length; + while (k-- + 1) { + array.push(undefined); + } + } + array.splice(newIndex, 0, array.splice(previousIndex, 1)[0]); + return array; +} + +export function omit(obj, ...keysToOmit) { + return Object.keys(obj).reduce((acc, key) => { + if (keysToOmit.indexOf(key) === -1) acc[key] = obj[key]; + return acc; + }, {}); +} + +export const events = { + start: ['touchstart', 'mousedown'], + move: ['touchmove', 'mousemove'], + end: ['touchend', 'touchcancel', 'mouseup'], +}; + +export const vendorPrefix = (function() { + if (typeof window === 'undefined' || typeof document === 'undefined') return ''; // server environment + // fix for: + // https://bugzilla.mozilla.org/show_bug.cgi?id=548397 + // window.getComputedStyle() returns null inside an iframe with display: none + // in this case return an array with a fake mozilla style in it. + const styles = window.getComputedStyle(document.documentElement, '') || ['-moz-hidden-iframe']; + const pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']))[1]; + + switch (pre) { + case 'ms': + return 'ms'; + default: + return pre && pre.length ? pre[0].toUpperCase() + pre.substr(1) : ''; + } +})(); + +export const closest = memoize((el, fn) => { + if (!el) return null; + if (fn(el)) return el; + return closest(el.parentNode, fn); +}); + +export function limit(min, max, value) { + if (value < min) { + return min; + } + if (value > max) { + return max; + } + return value; +} + +function getCSSPixelValue(stringValue) { + if (stringValue.substr(-2) === 'px') { + return parseFloat(stringValue); + } + return 0; +} + +export function getElementMargin(element) { + const style = window.getComputedStyle(element); + + return { + top: getCSSPixelValue(style.marginTop), + right: getCSSPixelValue(style.marginRight), + bottom: getCSSPixelValue(style.marginBottom), + left: getCSSPixelValue(style.marginLeft), + }; +} + +export function provideDisplayName(prefix, Component) { + const componentName = Component.displayName || Component.name; + + return componentName ? `${prefix}(${componentName})` : prefix; +} + +export function getPosition(event) { + if (event.touches && event.touches.length) { + return { + x: event.touches[0].pageX, + y: event.touches[0].pageY, + }; + } else if (event.changedTouches && event.changedTouches.length) { + return { + x: event.changedTouches[0].pageX, + y: event.changedTouches[0].pageY, + }; + } else { + return { + x: event.pageX, + y: event.pageY, + }; + } +} + +export function isTouchEvent(event) { + return ( + event.touches && event.touches.length || + event.changedTouches && event.changedTouches.length + ); +} + +export function getEdgeOffset(node, parent, offset = {top: 0, left: 0}) { + // Get the actual offsetTop / offsetLeft value, no matter how deep the node is nested + if (node) { + const nodeOffset = { + top: offset.top + node.offsetTop, + left: offset.left + node.offsetLeft, + }; + + if (node.parentNode !== parent) { + return getEdgeOffset(node.parentNode, parent, nodeOffset); + } else { + return nodeOffset; + } + } +} + + +export function getLockPixelOffset({lockOffset, width, height}) { + let offsetX = lockOffset; + let offsetY = lockOffset; + let unit = 'px'; + + if (typeof lockOffset === 'string') { + const match = /^[+-]?\d*(?:\.\d*)?(px|%)$/.exec(lockOffset); + + invariant( + match !== null, + 'lockOffset value should be a number or a string of a ' + + 'number followed by "px" or "%". Given %s', + lockOffset + ); + + offsetX = (offsetY = parseFloat(lockOffset)); + unit = match[1]; + } + + invariant( + isFinite(offsetX) && isFinite(offsetY), + 'lockOffset value should be a finite. Given %s', + lockOffset + ); + + if (unit === '%') { + offsetX = offsetX * width / 100; + offsetY = offsetY * height / 100; + } + + return { + x: offsetX, + y: offsetY, + }; +} From f85dbe0420c7ae6d8a4b0d7190da0e5f2143d959 Mon Sep 17 00:00:00 2001 From: Darkness <616487222@qq.com> Date: Mon, 10 Dec 2018 10:32:18 +0800 Subject: [PATCH 2/2] revert --- react-sortable-hoc/Manager.js | 77 -- react-sortable-hoc/SortableContainer/index.js | 969 ------------------ react-sortable-hoc/SortableElement/index.js | 94 -- react-sortable-hoc/SortableGroup/index.js | 100 -- react-sortable-hoc/SortableHandle/index.js | 31 - react-sortable-hoc/index.js | 10 - react-sortable-hoc/utils.js | 163 --- 7 files changed, 1444 deletions(-) delete mode 100755 react-sortable-hoc/Manager.js delete mode 100755 react-sortable-hoc/SortableContainer/index.js delete mode 100755 react-sortable-hoc/SortableElement/index.js delete mode 100644 react-sortable-hoc/SortableGroup/index.js delete mode 100755 react-sortable-hoc/SortableHandle/index.js delete mode 100755 react-sortable-hoc/index.js delete mode 100755 react-sortable-hoc/utils.js diff --git a/react-sortable-hoc/Manager.js b/react-sortable-hoc/Manager.js deleted file mode 100755 index ff1ffb0c2..000000000 --- a/react-sortable-hoc/Manager.js +++ /dev/null @@ -1,77 +0,0 @@ -import { get } from 'lodash-es'; -export default class Manager { - refs = {}; - group = {}; - - add(collection, ref, index) { - if (!this.refs[collection]) { - this.refs[collection] = []; - } - if (this.start != null && this.start.index === index) { - this.start.node = ref.node; - } - - this.refs[collection].push(ref); - } - - remove(collection, ref) { - const index = this.getIndex(collection, ref); - - if (index !== -1) { - this.refs[collection].splice(index, 1); - } - } - - setGroup = (group, node) => { - this.group[group] = node; - } - - - refreshAllGroup = () => { - const groupKeys = Object.keys(this.group); - groupKeys.forEach((groupKey) => { - this.group[groupKey].sortGroupInfo.reRenderer - && this.group[groupKey].sortGroupInfo.reRenderer(); - }); - } - - getIsGroupDisabled = (collection) => { - const startManager = get(this, ['start'], null); - const startCollection = get(startManager, ['collection'], null); - const sortGroup = get(this, ['group', collection, 'sortGroupInfo', 'sortGroup'], {}); - if (startCollection != null - && startCollection !== collection - && !sortGroup[startCollection] - ) { - return true; - } - return false; - } - - isActive() { - return this.active; - } - - getActive() { - return this.refs[this.active.collection].find( - // eslint-disable-next-line eqeqeq - ({node}) => node.sortableInfo.index == this.active.index - ); - } - - getIndex(collection, ref) { - return this.refs[collection].indexOf(ref); - } - - getOrderedRefs(collection = this.active.collection) { - const orederedRefs = get(this.refs, [collection], []); - return orederedRefs.sort(sortByIndex); - } -} - -function sortByIndex( - {node: {sortableInfo: {index: index1}}}, - {node: {sortableInfo: {index: index2}}} -) { - return (index1 - index2); -} diff --git a/react-sortable-hoc/SortableContainer/index.js b/react-sortable-hoc/SortableContainer/index.js deleted file mode 100755 index b52007810..000000000 --- a/react-sortable-hoc/SortableContainer/index.js +++ /dev/null @@ -1,969 +0,0 @@ -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import {findDOMNode} from 'react-dom'; -import invariant from 'invariant'; - -import Manager from '../Manager'; -import sortManager from '../../sort_manager'; -import { - closest, - events, - vendorPrefix, - limit, - getEdgeOffset, - getElementMargin, - getLockPixelOffset, - getPosition, - isTouchEvent, - provideDisplayName, - omit, -} from '../utils'; - -// Export Higher Order Sortable Container Component -export default function sortableContainer(WrappedComponent, config = {withRef: false}) { - return class extends Component { - constructor(props) { - super(props); - this.manager = new Manager(); - this.events = { - start: this.handleStart, - move: this.handleMove, - end: this.handleEnd, - }; - - invariant( - !(props.distance && props.pressDelay), - 'Attempted to set both `pressDelay` and `distance` on SortableContainer, you may only use one or the other, not both at the same time.' - ); - - this.state = {}; - } - - static displayName = provideDisplayName('sortableList', WrappedComponent); - - static defaultProps = { - axis: () => { - return { - axis: 'y', - lockAxis: null, - } - }, - transitionDuration: 300, - pressDelay: 0, - pressThreshold: 5, - distance: 0, - hideSortableGhost: true, - shouldCancelStart: function(e) { - // Cancel sorting if the event target is an `input`, `textarea`, `select` or `option` - const disabledElements = ['input', 'textarea', 'select', 'option', 'button']; - - if (disabledElements.indexOf(e.target.tagName.toLowerCase()) !== -1) { - return true; // Return true to cancel sorting - } - }, - lockToContainerEdges: false, - lockOffset: '50%', - getHelperDimensions: ({node}) => ({ - width: node.offsetWidth, - height: node.offsetHeight, - }), - }; - - static propTypes = { - axis: PropTypes.func, - distance: PropTypes.number, - lockAxis: PropTypes.string, - helperClass: PropTypes.string, - transitionDuration: PropTypes.number, - onSortStart: PropTypes.func, - onSortMove: PropTypes.func, - onSortOver: PropTypes.func, - onSortEnd: PropTypes.func, - shouldCancelStart: PropTypes.func, - pressDelay: PropTypes.number, - useDragHandle: PropTypes.bool, - hideSortableGhost: PropTypes.bool, - lockToContainerEdges: PropTypes.bool, - lockOffset: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - PropTypes.arrayOf( - PropTypes.oneOfType([PropTypes.number, PropTypes.string]) - ), - ]), - getContainer: PropTypes.func, - getHelperDimensions: PropTypes.func, - }; - - static childContextTypes = { - manager: PropTypes.object.isRequired, - }; - - getChildContext() { - return { - manager: this.manager, - }; - } - - componentDidMount() { - - /* - * Set our own default rather than using defaultProps because Jest - * snapshots will serialize window, causing a RangeError - * https://github.com/clauderic/react-sortable-hoc/issues/249 - */ - - const container = this.getContainer(); - - Promise.resolve(container).then((containerNode) => { - this.container = containerNode; - this.document = this.container.ownerDocument || document; - const contentWindow = this.document.defaultView; - this.contentWindow = typeof contentWindow === 'function' - ? contentWindow() - : contentWindow; - - for (const key in this.events) { - if (this.events.hasOwnProperty(key)) { - events[key].forEach(eventName => - this.container.addEventListener(eventName, this.events[key], false) - ); - } - } - }); - } - - componentWillUnmount() { - if (this.container) { - for (const key in this.events) { - if (this.events.hasOwnProperty(key)) { - events[key].forEach(eventName => - this.container.removeEventListener(eventName, this.events[key]) - ); - } - } - } - } - - checkSortableInfo = el => el.sortableInfo != null; - checkSortableHandle = el => el.sortableHandle != null; - checkSortableGroup = el => el.sortGroupInfo != null; - - - handleStart = event => { - const {distance, shouldCancelStart, useDragHandle} = this.props; - - if (event.button === 2 || shouldCancelStart(event)) { - return false; - } - - this._touched = true; - this._pos = getPosition(event); - - const node = closest(event.target, this.checkSortableInfo); - const groupNode = closest(event.target, this.checkSortableGroup); - - if ( - node && - node.sortableInfo && - this.nodeIsChild(node) && - !this.state.sorting - ) { - const {index, collection} = node.sortableInfo; - const dimensions = this.props.getHelperDimensions({ index, collection, node }); - this.manager.active = {index, collection, node, groupNode}; - this.manager.start = {index, collection, node, dimensions }; - this.manager.refreshAllGroup(); - if ( - useDragHandle && !closest(event.target, this.checkSortableHandle) - ) - return; - - /* - * Fixes a bug in Firefox where the :active state of anchor tags - * prevent subsequent 'mousemove' events from being fired - * (see https://github.com/clauderic/react-sortable-hoc/issues/118) - */ - if (!isTouchEvent(event) && event.target.tagName.toLowerCase() === 'a') { - event.preventDefault(); - } - - if (!distance) { - if (this.props.pressDelay === 0) { - this.handlePress(event); - } else { - this.pressTimer = setTimeout( - () => this.handlePress(event), - this.props.pressDelay - ); - } - } - } - }; - - nodeIsChild = node => { - return node.sortableInfo.manager === this.manager; - }; - - clearNodeTransform = (nodes) => { - if (!nodes) return; - for (let i = 0, len = nodes.length; i < len; i += 1) { - const node = nodes[i]; - const el = node.node; - - // Clear the cached offsetTop / offsetLeft value - node.edgeOffset = null; - - // Remove the transforms / transitions - el.style[`${vendorPrefix}Transform`] = ''; - el.style[`${vendorPrefix}TransitionDuration`] = ''; - } - } - handleMove = event => { - const {distance, pressThreshold} = this.props; - - const sortableGroup = closest(event.target, this.checkSortableGroup); - if (sortableGroup != null && - this.manager.active != null && - sortableGroup.sortGroupInfo.sortGroup[this.manager.start.collection] && - sortableGroup.sortGroupInfo.collection !== this.manager.active.collection) { - const newCollection = sortableGroup.sortGroupInfo.collection; - const oldCollection = this.manager.active.collection; - const oldNodes = this.manager.refs[oldCollection]; - this.clearNodeTransform(oldNodes); - let nextIndex = this.manager.start.index; - if (newCollection !== this.manager.start.collection) { - nextIndex = sortableGroup.sortGroupInfo.collectionCount; - } - this.offsetEdge.top -= this.lastScrollTop; - this.offsetEdge.left -= this.lastScrollLeft; - this.changeTranslate(sortableGroup); - this.initScroll(sortableGroup); - this.offsetEdge.top += this.scrollContainer.scrollTop; - this.offsetEdge.left += this.scrollContainer.scrollLeft; - - this.manager.active = { - index: nextIndex, - collection: newCollection, - node: this.manager.start.node, - groupNode: sortableGroup, - }; - this.manager.group[oldCollection].sortGroupInfo.reRenderer(); - this.manager.group[newCollection].sortGroupInfo.reRenderer(); - this.index = nextIndex; - this.newIndex = null; - this.handleInitNewCollectionStatus(); - } - - if (!this.state.sorting && this._touched) { - const position = getPosition(event); - const delta = this._delta = { - x: this._pos.x - position.x, - y: this._pos.y - position.y, - }; - const combinedDelta = Math.abs(delta.x) + Math.abs(delta.y); - - if (!distance && (!pressThreshold || pressThreshold && combinedDelta >= pressThreshold)) { - clearTimeout(this.cancelTimer); - this.cancelTimer = setTimeout(this.cancel, 0); - } else if (distance && combinedDelta >= distance && this.manager.isActive()) { - this.handlePress(event); - } - } - }; - - handleEnd = () => { - this._touched = false; - this.cancel(); - }; - - cancel = () => { - const {distance} = this.props; - const {sorting} = this.state; - - if (!sorting) { - if (!distance) { - clearTimeout(this.pressTimer); - } - this.manager.active = null; - } - }; - - lastScrollTop = 0; - lastScrollLeft = 0; - initTranslate = (node, container, isOutContainer = false) => { - const containerBoundingRect = container.getBoundingClientRect(); - const nodeBoundingRect = node.getBoundingClientRect(); - const minTranslate = {}; - const maxTranslate = {}; - let axis = this.axis; - if (isOutContainer) { - axis = this.containerAxis; - } - if (axis.x) { - minTranslate.x = containerBoundingRect.left - - nodeBoundingRect.left - - this.width / 2; - maxTranslate.x = containerBoundingRect.left + containerBoundingRect.width - - nodeBoundingRect.left - - this.width / 2; - } - if (axis.y) { - minTranslate.y = containerBoundingRect.top - - nodeBoundingRect.top - - this.height / 2; - maxTranslate.y = containerBoundingRect.top + containerBoundingRect.height - - nodeBoundingRect.top - - this.height / 2; - } - if (isOutContainer) { - this.outContainer = {}; - this.outContainer.minTranslate = minTranslate; - this.outContainer.maxTranslate = maxTranslate; - } else { - this.scrollContainer = container; - this.lastScrollTop = this.scrollContainer.scrollTop; - this.lastScrollLeft = this.scrollContainer.lastScrollLeft; - this.minTranslate = minTranslate; - this.maxTranslate = maxTranslate; - this.boundingClientRect = nodeBoundingRect; - this.containerBoundingRect = containerBoundingRect; - this.initTranslate(node, this.container, true) - } - } - - changeTranslate = (container) => { - this.scrollContainer = container; - const containerBoundingRect = container.getBoundingClientRect(); - if (this.axis.x) { - const oldTranslateMinx = this.minTranslate.x; - const oldContainerWidth = this.containerBoundingRect.width; - const oldContainerLeft = this.containerBoundingRect.left; - const newContainerWidth = containerBoundingRect.width; - const newContainerLeft = containerBoundingRect.left; - const oldTranslateMaxx = this.maxTranslate.x; - this.minTranslate.x = oldTranslateMinx - (newContainerLeft - oldContainerLeft); - this.maxTranslate.x = oldTranslateMaxx + ( - newContainerWidth + newContainerLeft - oldContainerWidth - oldContainerLeft - ) - } - if (this.axis.y) { - const oldTranslateMiny = this.minTranslate.y; - const oldContainerHeight = this.containerBoundingRect.height; - const oldContainerTop = this.containerBoundingRect.top; - const newContainerHeight = containerBoundingRect.height; - const newContainerTop = containerBoundingRect.top; - - const oldTranslateMaxy = this.maxTranslate.y; - - this.minTranslate.y = oldTranslateMiny - (newContainerTop - oldContainerTop); - this.maxTranslate.y = oldTranslateMaxy + ( - newContainerHeight + newContainerTop - oldContainerHeight - oldContainerTop - ) - } - - this.containerBoundingRect = containerBoundingRect; - - this.lastScrollTop = this.scrollContainer.scrollTop; - this.lastScrollLeft = this.scrollContainer.lastScrollLeft; - } - - - - - initScroll = (container) => { - this.initialScroll = { - top: container.scrollTop, - left: container.scrollLeft, - }; - } - handlePress = event => { - const active = this.manager.getActive(); - - if (active) { - const { - getHelperDimensions, - helperClass, - hideSortableGhost, - onSortStart, - } = this.props; - const {node} = active; - // this.sortableGhost = node; - const {index, collection} = node.sortableInfo; - const margin = getElementMargin(node); - const groupNode = closest(node, this.checkSortableGroup); - const container = groupNode || this.container; - - - const dimensions = getHelperDimensions({index, node, collection}); - this.width = dimensions.width; - this.height = dimensions.height; - - const axis = this.props.axis(collection).axis; - this.axis = { - x: axis.indexOf('x') >= 0, - y: axis.indexOf('y') >= 0, - }; - const containerAxis = this.props.axis().axis; - this.containerAxis = { - x: containerAxis.indexOf('x') >= 0, - y: containerAxis.indexOf('y') >= 0, - }; - - this.initTranslate(node, container); - - this.lastTransform = { - x: 0, - y: 0, - } - - this.node = node; - this.margin = margin; - this.marginOffset = { - x: this.margin.left + this.margin.right, - y: Math.max(this.margin.top, this.margin.bottom), - }; - - this.index = index; - this.newIndex = index; - - this.offsetEdge = getEdgeOffset(node, this.container); - this.initialOffset = getPosition(event); - this.initScroll(container); - - this.initialWindowScroll = { - top: window.pageYOffset, - left: window.pageXOffset, - }; - - const fields = node.querySelectorAll('input, textarea, select'); - const clonedNode = node.cloneNode(true); - const clonedFields = [ - ...clonedNode.querySelectorAll('input, textarea, select'), - ]; // Convert NodeList to Array - - clonedFields.forEach((field, index) => { - if (field.type !== 'file' && fields[index]) { - field.value = fields[index].value; - } - }); - - this.helper = this.document.body.appendChild(clonedNode); - - this.helper.style.position = 'fixed'; - this.helper.style.top = `${this.boundingClientRect.top - margin.top}px`; - this.helper.style.left = `${this.boundingClientRect.left - margin.left}px`; - this.helper.style.width = `${this.width}px`; - this.helper.style.height = `${this.height}px`; - this.helper.style.boxSizing = 'border-box'; - this.helper.style.pointerEvents = 'none'; - - // if (hideSortableGhost) { - // this.sortableGhost = node; - // node.style.visibility = 'hidden'; - // node.style.opacity = 0; - // } - - - - if (helperClass) { - this.helper.classList.add(...helperClass.split(' ')); - } - - this.listenerNode = event.touches ? node : this.contentWindow; - events.move.forEach(eventName => - this.listenerNode.addEventListener( - eventName, - this.handleSortMove, - false - )); - events.end.forEach(eventName => - this.listenerNode.addEventListener( - eventName, - this.handleSortEnd, - false - )); - - this.setState({ - sorting: true, - sortingIndex: index, - }); - - if (onSortStart) { - onSortStart({node, index, collection}, event); - } - } - }; - - handleSortMove = event => { - const {onSortMove} = this.props; - event.preventDefault(); // Prevent scrolling on mobile - - this.updatePosition(event); - this.animateNodes(); - this.autoscroll(event); - - if (onSortMove) { - onSortMove(event); - } - }; - - handleInitNewCollectionStatus = () => { - clearInterval(this.autoscrollInterval); - this.autoscrollInterval = null; - this.isAutoScrolling = false; - } - - handleSortEnd = event => { - const {hideSortableGhost, onSortEnd} = this.props; - const {collection} = this.manager.active; - const oldCollection = this.manager.start.collection; - const oldIndex = this.manager.start.index; - - const startNode = this.manager.start.node; - startNode.style.visibility = ''; - startNode.style.opacity = ''; - - // Remove the event listeners if the node is still in the DOM - if (this.listenerNode) { - events.move.forEach(eventName => - this.listenerNode.removeEventListener( - eventName, - this.handleSortMove - )); - events.end.forEach(eventName => - this.listenerNode.removeEventListener(eventName, this.handleSortEnd)); - } - - // Remove the helper from the DOM - this.helper.parentNode.removeChild(this.helper); - - if (hideSortableGhost && this.sortableGhost) { - this.sortableGhost.style.visibility = ''; - this.sortableGhost.style.opacity = ''; - } - - const nodes = this.manager.refs[collection]; - this.clearNodeTransform(nodes); - const oldNodes = this.manager.refs[oldCollection]; - oldNodes.forEach((node) => { - node.node.style.visibility = ''; - node.node.style.opacity = ''; - }) - // this.clearNodeTransform(oldNodes); - // if (oldNodes[oldIndex] && oldNodes[oldIndex].node) { - // oldNodes[oldIndex].node.style.visibility = ''; - // oldNodes[oldIndex].node.style.opacity = ''; - // } - this.handleInitNewCollectionStatus(); - - // Update state - this.manager.active = null; - this.manager.start = null; - - this.setState({ - sorting: false, - sortingIndex: null, - }); - - this.manager.refreshAllGroup(); - - if (typeof onSortEnd === 'function') { - onSortEnd( - { - oldIndex: oldIndex, - newIndex: this.newIndex, - oldCollection, - newCollection: collection, - }, - event - ); - } - - this._touched = false; - }; - - getLockPixelOffsets() { - const {width, height} = this; - const {lockOffset} = this.props; - const offsets = Array.isArray(lockOffset) - ? lockOffset - : [lockOffset, lockOffset]; - - invariant( - offsets.length === 2, - 'lockOffset prop of SortableContainer should be a single ' + - 'value or an array of exactly two values. Given %s', - lockOffset - ); - - const [minLockOffset, maxLockOffset] = offsets; - - return [ - getLockPixelOffset({lockOffset: minLockOffset, width, height}), - getLockPixelOffset({lockOffset: maxLockOffset, width, height}), - ]; - } - - updatePosition(event) { - const {lockToContainerEdges} = this.props; - const lockAxis = this.props.axis(this.manager.start.collection).lockAxis; - const offset = getPosition(event); - const translate = { - x: offset.x - this.initialOffset.x, - y: offset.y - this.initialOffset.y, - }; - - // Adjust for window scroll - translate.y -= (window.pageYOffset - this.initialWindowScroll.top); - translate.x -= (window.pageXOffset - this.initialWindowScroll.left); - - this.translate = translate; - - if (lockToContainerEdges) { - const [minLockOffset, maxLockOffset] = this.getLockPixelOffsets(); - const minOffset = { - x: this.width / 2 - minLockOffset.x, - y: this.height / 2 - minLockOffset.y, - }; - const maxOffset = { - x: this.width / 2 - maxLockOffset.x, - y: this.height / 2 - maxLockOffset.y, - }; - - translate.x = limit( - this.minTranslate.x + minOffset.x, - this.maxTranslate.x - maxOffset.x, - translate.x - ); - translate.y = limit( - this.minTranslate.y + minOffset.y, - this.maxTranslate.y - maxOffset.y, - translate.y - ); - } - - if (lockAxis === 'x') { - translate.y = 0; - } else if (lockAxis === 'y') { - translate.x = 0; - } - - this.helper.style[ - `${vendorPrefix}Transform` - ] = `translate3d(${translate.x}px,${translate.y}px, 0)`; - } - - animateNodes() { - const {transitionDuration, hideSortableGhost, onSortOver} = this.props; - const nodes = this.manager.getOrderedRefs(); - const containerScrollDelta = { - left: this.scrollContainer.scrollLeft - this.initialScroll.left, - top: this.scrollContainer.scrollTop - this.initialScroll.top, - }; - const sortingOffset = { - left: this.offsetEdge.left + this.translate.x + containerScrollDelta.left, - top: this.offsetEdge.top + this.translate.y + containerScrollDelta.top, - }; - const windowScrollDelta = { - top: (window.pageYOffset - this.initialWindowScroll.top), - left: (window.pageXOffset - this.initialWindowScroll.left), - }; - const prevIndex = this.newIndex; - this.newIndex = null; - this.lastTransform = { - x: 0, - y: 0, - } - - for (let i = 0, len = nodes.length; i < len; i++) { - const {node} = nodes[i]; - const index = node.sortableInfo.index; - const width = node.offsetWidth; - const height = node.offsetHeight; - const offset = { - width: this.width > width ? width / 2 : this.width / 2, - height: this.height > height ? height / 2 : this.height / 2, - }; - - const translate = { - x: 0, - y: 0, - }; - let {edgeOffset} = nodes[i]; - - // If we haven't cached the node's offsetTop / offsetLeft value - if (!edgeOffset) { - nodes[i].edgeOffset = (edgeOffset = getEdgeOffset(node, this.container)); - } - - // Get a reference to the next and previous node - const nextNode = i < nodes.length - 1 && nodes[i + 1]; - const prevNode = i > 0 && nodes[i - 1]; - - // Also cache the next node's edge offset if needed. - // We need this for calculating the animation in a grid setup - if (nextNode && !nextNode.edgeOffset) { - nextNode.edgeOffset = getEdgeOffset(nextNode.node, this.container); - } - - // If the node is the one we're currently animating, skip it - const thisIndex = this.manager.active.index; - if (index === thisIndex) { - this.sortableGhost = node; - if (hideSortableGhost) { - /* - * With windowing libraries such as `react-virtualized`, the sortableGhost - * node may change while scrolling down and then back up (or vice-versa), - * so we need to update the reference to the new node just to be safe. - */ - node.style.visibility = 'hidden'; - node.style.opacity = 0; - } - continue; - } - - if (transitionDuration) { - node.style[ - `${vendorPrefix}TransitionDuration` - ] = `${transitionDuration}ms`; - } - - if (this.axis.x) { - if (this.axis.y) { - // Calculations for a grid setup - if ( - index < thisIndex && - ( - ((sortingOffset.left + windowScrollDelta.left) - offset.width <= edgeOffset.left && - (sortingOffset.top + windowScrollDelta.top) <= edgeOffset.top + offset.height) || - (sortingOffset.top + windowScrollDelta.top) + offset.height <= edgeOffset.top - ) - ) { - // If the current node is to the left on the same row, or above the node that's being dragged - // then move it to the right - translate.x = this.width + this.marginOffset.x; - if ( - edgeOffset.left + translate.x > - this.containerBoundingRect.width - offset.width - ) { - // If it moves passed the right bounds, then animate it to the first position of the next row. - // We just use the offset of the next node to calculate where to move, because that node's original position - // is exactly where we want to go - translate.x = nextNode.edgeOffset.left - edgeOffset.left; - translate.y = nextNode.edgeOffset.top - edgeOffset.top; - } - if (this.newIndex === null) { - this.newIndex = index; - } - } else if ( - index > thisIndex && - ( - ((sortingOffset.left + windowScrollDelta.left) + offset.width >= edgeOffset.left && - (sortingOffset.top + windowScrollDelta.top) + offset.height >= edgeOffset.top) || - (sortingOffset.top + windowScrollDelta.top) + offset.height >= edgeOffset.top + height - ) - ) { - // If the current node is to the right on the same row, or below the node that's being dragged - // then move it to the left - translate.x = -(this.width + this.marginOffset.x); - if ( - edgeOffset.left + translate.x < - this.containerBoundingRect.left + offset.width - ) { - // If it moves passed the left bounds, then animate it to the last position of the previous row. - // We just use the offset of the previous node to calculate where to move, because that node's original position - // is exactly where we want to go - translate.x = prevNode.edgeOffset.left - edgeOffset.left; - translate.y = prevNode.edgeOffset.top - edgeOffset.top; - } - this.newIndex = index; - } - } else { - if ( - index > thisIndex && - (sortingOffset.left + windowScrollDelta.left) + offset.width >= edgeOffset.left - ) { - translate.x = -(this.width + this.marginOffset.x); - this.newIndex = index; - } else if ( - index < thisIndex && - (sortingOffset.left + windowScrollDelta.left) <= edgeOffset.left + offset.width - ) { - translate.x = this.width + this.marginOffset.x; - if (this.newIndex == null) { - this.newIndex = index; - } - } - } - } else if (this.axis.y) { - if ( - index > thisIndex && - (sortingOffset.top + windowScrollDelta.top) + offset.height >= edgeOffset.top - ) { - translate.y = -(this.height + this.marginOffset.y); - this.newIndex = index; - } else if ( - index < thisIndex && - (sortingOffset.top + windowScrollDelta.top) <= edgeOffset.top + offset.height - ) { - translate.y = this.height + this.marginOffset.y; - if (this.newIndex == null) { - this.newIndex = index; - } - } - } - // const nodeStyle = this.props.getHelperDimensions({ node }); - // if (translate.x > 0) { - // this.lastTransform.x -= nodeStyle.width; - // } - // if (translate.y > 0) { - // this.lastTransform.y -= nodeStyle.height; - // } - // if (translate.x < 0) { - // this.lastTransform.x += nodeStyle.width; - // } - // if (translate.y < 0) { - // this.lastTransform.y += nodeStyle.height; - // } - node.style[`${vendorPrefix}Transform`] = `translate3d(${translate.x}px,${translate.y}px,0)`; - } - // this.sortableGhost.style[`${vendorPrefix}Transform`] = `translate3d(${this.lastTransform.x}px,${this.lastTransform.y}px,0)`; - - if (this.newIndex == null) { - this.newIndex = this.index; - } - - if (onSortOver && this.newIndex !== prevIndex) { - onSortOver({ - newIndex: this.newIndex, - oldIndex: prevIndex, - index: this.index, - oldCollection: this.manager.start.collection, - collection: this.manager.active.collection, - }); - } - } - - autoscroll = ( - event, - scrollContainer = this.scrollContainer, - maxTranslate = this.maxTranslate, - minTranslate = this.minTranslate, - isOutContainer = false, - ) => { - const translate = this.translate; - const direction = { - x: 0, - y: 0, - }; - const speed = { - x: 1, - y: 1, - }; - const acceleration = { - x: 10, - y: 10, - }; - - if (translate.y >= maxTranslate.y - this.height / 2) { - direction.y = 1; // Scroll Down - speed.y = acceleration.y * Math.abs((maxTranslate.y - this.height / 2 - translate.y) / this.height); - } else if (translate.x >= maxTranslate.x - this.width / 2) { - direction.x = 1; // Scroll Right - speed.x = acceleration.x * Math.abs((maxTranslate.x - this.width / 2 - translate.x) / this.width); - } else if (translate.y <= minTranslate.y + this.height / 2) { - direction.y = -1; // Scroll Up - speed.y = acceleration.y * Math.abs((translate.y - this.height / 2 - minTranslate.y) / this.height); - } else if (translate.x <= minTranslate.x + this.width / 2) { - direction.x = -1; // Scroll Left - speed.x = acceleration.x * Math.abs((translate.x - this.width / 2 - minTranslate.x) / this.width); - } - - if (this.autoscrollInterval) { - clearInterval(this.autoscrollInterval); - this.autoscrollInterval = null; - this.isAutoScrolling = false; - } - - if (direction.x !== 0 || direction.y !== 0) { - this.autoscrollInterval = setInterval( - () => { - this.isAutoScrolling = true; - const offset = { - top: 1 * speed.y * direction.y, - left: 1 * speed.x * direction.x, - }; - // const scrollContainer = this.manager.active.groupNode || this.scrollContainer; - scrollContainer.scrollLeft += offset.left; - scrollContainer.scrollTop += offset.top; - this.translate.y += offset.top; - this.translate.x += offset.left; - this.animateNodes(); - }, - 5 - ); - } - // console.log('direction', direction.x, direction.y, !isOutContainer); - if (direction.x === 0 && direction.y === 0 && !isOutContainer) { - this.autoscroll( - event, - this.container, - this.outContainer.maxTranslate, - this.outContainer.minTranslate, - true - ) - } - }; - - getWrappedInstance() { - invariant( - config.withRef, - 'To access the wrapped instance, you need to pass in {withRef: true} as the second argument of the SortableContainer() call' - ); - - return this.refs.wrappedInstance; - } - - getContainer() { - const {getContainer} = this.props; - - if (typeof getContainer !== 'function') { - return findDOMNode(this); - } - - return getContainer(config.withRef ? this.getWrappedInstance() : undefined); - } - - render() { - const ref = config.withRef ? 'wrappedInstance' : null; - - return ( - - ); - } - }; -} - diff --git a/react-sortable-hoc/SortableElement/index.js b/react-sortable-hoc/SortableElement/index.js deleted file mode 100755 index 917fc25c2..000000000 --- a/react-sortable-hoc/SortableElement/index.js +++ /dev/null @@ -1,94 +0,0 @@ -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import {findDOMNode} from 'react-dom'; -import invariant from 'invariant'; - -import {provideDisplayName, omit} from '../utils'; - -// Export Higher Order Sortable Element Component -export default function sortableElement(WrappedComponent, config = {withRef: false}) { - return class extends Component { - static displayName = provideDisplayName('sortableElement', WrappedComponent); - - static contextTypes = { - manager: PropTypes.object.isRequired, - }; - - static propTypes = { - index: PropTypes.number.isRequired, - collection: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - disabled: PropTypes.bool, - }; - - static defaultProps = { - collection: 0, - }; - - componentDidMount() { - const {collection, disabled, index} = this.props; - - if (!disabled) { - this.setDraggable(collection, index); - } - } - - componentWillReceiveProps(nextProps) { - if (this.props.index !== nextProps.index && this.node) { - this.node.sortableInfo.index = nextProps.index; - } - if (this.props.disabled !== nextProps.disabled) { - const {collection, disabled, index} = nextProps; - if (disabled) { - this.removeDraggable(collection); - } else { - this.setDraggable(collection, index); - } - } else if (this.props.collection !== nextProps.collection) { - this.removeDraggable(this.props.collection); - this.setDraggable(nextProps.collection, nextProps.index); - } - } - - componentWillUnmount() { - const {collection, disabled} = this.props; - - if (!disabled) this.removeDraggable(collection); - } - - setDraggable(collection, index) { - const node = (this.node = findDOMNode(this)); - - node.sortableInfo = { - index, - collection, - manager: this.context.manager, - }; - - this.ref = {node}; - this.context.manager.add(collection, this.ref, index); - } - - removeDraggable(collection) { - this.context.manager.remove(collection, this.ref); - } - - getWrappedInstance() { - invariant( - config.withRef, - 'To access the wrapped instance, you need to pass in {withRef: true} as the second argument of the SortableElement() call' - ); - return this.refs.wrappedInstance; - } - - render() { - const ref = config.withRef ? 'wrappedInstance' : null; - - return ( - - ); - } - }; -} diff --git a/react-sortable-hoc/SortableGroup/index.js b/react-sortable-hoc/SortableGroup/index.js deleted file mode 100644 index 17ec4143c..000000000 --- a/react-sortable-hoc/SortableGroup/index.js +++ /dev/null @@ -1,100 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { get, set } from 'lodash-es'; -import { findDOMNode } from 'react-dom'; - -export default class SortableGroup extends Component { - static contextTypes = { - manager: PropTypes.object.isRequired, - }; - - static propTypes = { - sortGroup: PropTypes.any, - collection: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - collectionCount: PropTypes.number, - defaultNodeHeight: PropTypes.number, - isDisabledChecker: PropTypes.func, - onReRenderer: PropTypes.func, - }; - - static defaultProps = { - collection: 0, - collectionCount: 0, - defaultNodeHeight: 75, - sortGroup: {}, - isDisabledChecker: () => { return true; }, - onReRenderer: () => {}, - }; - - state = { - activeCollection: null, - startCollection: null, - } - - componentDidMount() { - const { collection } = this.props; - this.setDragGroup(collection); - } - - componentWillReceiveProps(nextProps) { - if (nextProps.collectionCount !== this.props.collectionCount) { - this.refreshCollectionCount(nextProps.collectionCount); - } - } - - refreshCollectionCount = (count) => { - set(this.context, ['manager', 'group', this.props.collection, 'sortGroupInfo', 'collectionCount'], count); - } - - setDragGroup = (collection) => { - const node = (this.node = findDOMNode(this)); - node.sortGroupInfo = { - collection, - sortGroup: this.props.sortGroup, - collectionCount: this.props.collectionCount, - reRenderer: this.handleReRenderer, - }; - this.context.manager.setGroup(collection, node); - this.ref = { node }; - } - - handleReRenderer = () => { - const manager = this.context.manager; - const startManager = get(manager, ['start'], null); - const startCollection = get(startManager, ['collection'], null); - const activeManager = get(manager, ['active'], null); - const activeCollection = get(activeManager, ['collection'], null); - this.setState({ - activeCollection, - startCollection, - }); - this.props.onReRenderer(); - } - - render() { - let placeholderNode = null; - const manager = this.context.manager; - const startManager = get(manager, ['start'], null); - const startCollection = this.state.startCollection; - - const disabled = manager.getIsGroupDisabled(this.props.collection) - && this.props.isDisabledChecker(startCollection); - - const activeCollection = this.state.activeCollection; - if (activeCollection != null && activeCollection === this.props.collection - && startCollection !== this.props.collection - ) { - placeholderNode = get(startManager, ['node'], null); - } - const placeholderDimensions = get(startManager, 'dimensions', { - height: this.props.defaultNodeHeight, - width: 0, - }); - return this.props.children({ - placeholderNode, - placeholderDimensions, - disabled, - updateStamp: this.state.updateStamp, - }); - } -} diff --git a/react-sortable-hoc/SortableHandle/index.js b/react-sortable-hoc/SortableHandle/index.js deleted file mode 100755 index 4782999ab..000000000 --- a/react-sortable-hoc/SortableHandle/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import React, {Component} from 'react'; -import {findDOMNode} from 'react-dom'; -import invariant from 'invariant'; - -import {provideDisplayName} from '../utils'; - -// Export Higher Order Sortable Element Component -export default function sortableHandle(WrappedComponent, config = {withRef: false}) { - return class extends Component { - static displayName = provideDisplayName('sortableHandle', WrappedComponent); - - componentDidMount() { - const node = findDOMNode(this); - node.sortableHandle = true; - } - - getWrappedInstance() { - invariant( - config.withRef, - 'To access the wrapped instance, you need to pass in {withRef: true} as the second argument of the SortableHandle() call' - ); - return this.refs.wrappedInstance; - } - - render() { - const ref = config.withRef ? 'wrappedInstance' : null; - - return ; - } - }; -} diff --git a/react-sortable-hoc/index.js b/react-sortable-hoc/index.js deleted file mode 100755 index 8df6b570a..000000000 --- a/react-sortable-hoc/index.js +++ /dev/null @@ -1,10 +0,0 @@ -export SortableContainer from './SortableContainer'; -export SortableElement from './SortableElement'; -export SortableHandle from './SortableHandle'; -export SortableGroup from './SortableGroup'; - -export sortableContainer from './SortableContainer'; -export sortableElement from './SortableElement'; -export sortableHandle from './SortableHandle'; - -export {arrayMove} from './utils'; diff --git a/react-sortable-hoc/utils.js b/react-sortable-hoc/utils.js deleted file mode 100755 index 113c72fe0..000000000 --- a/react-sortable-hoc/utils.js +++ /dev/null @@ -1,163 +0,0 @@ -import invariant from 'invariant'; -import memoize from 'memoizee'; - -export function arrayMove(arr, previousIndex, newIndex) { - const array = arr.slice(0); - if (newIndex >= array.length) { - let k = newIndex - array.length; - while (k-- + 1) { - array.push(undefined); - } - } - array.splice(newIndex, 0, array.splice(previousIndex, 1)[0]); - return array; -} - -export function omit(obj, ...keysToOmit) { - return Object.keys(obj).reduce((acc, key) => { - if (keysToOmit.indexOf(key) === -1) acc[key] = obj[key]; - return acc; - }, {}); -} - -export const events = { - start: ['touchstart', 'mousedown'], - move: ['touchmove', 'mousemove'], - end: ['touchend', 'touchcancel', 'mouseup'], -}; - -export const vendorPrefix = (function() { - if (typeof window === 'undefined' || typeof document === 'undefined') return ''; // server environment - // fix for: - // https://bugzilla.mozilla.org/show_bug.cgi?id=548397 - // window.getComputedStyle() returns null inside an iframe with display: none - // in this case return an array with a fake mozilla style in it. - const styles = window.getComputedStyle(document.documentElement, '') || ['-moz-hidden-iframe']; - const pre = (Array.prototype.slice.call(styles).join('').match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']))[1]; - - switch (pre) { - case 'ms': - return 'ms'; - default: - return pre && pre.length ? pre[0].toUpperCase() + pre.substr(1) : ''; - } -})(); - -export const closest = memoize((el, fn) => { - if (!el) return null; - if (fn(el)) return el; - return closest(el.parentNode, fn); -}); - -export function limit(min, max, value) { - if (value < min) { - return min; - } - if (value > max) { - return max; - } - return value; -} - -function getCSSPixelValue(stringValue) { - if (stringValue.substr(-2) === 'px') { - return parseFloat(stringValue); - } - return 0; -} - -export function getElementMargin(element) { - const style = window.getComputedStyle(element); - - return { - top: getCSSPixelValue(style.marginTop), - right: getCSSPixelValue(style.marginRight), - bottom: getCSSPixelValue(style.marginBottom), - left: getCSSPixelValue(style.marginLeft), - }; -} - -export function provideDisplayName(prefix, Component) { - const componentName = Component.displayName || Component.name; - - return componentName ? `${prefix}(${componentName})` : prefix; -} - -export function getPosition(event) { - if (event.touches && event.touches.length) { - return { - x: event.touches[0].pageX, - y: event.touches[0].pageY, - }; - } else if (event.changedTouches && event.changedTouches.length) { - return { - x: event.changedTouches[0].pageX, - y: event.changedTouches[0].pageY, - }; - } else { - return { - x: event.pageX, - y: event.pageY, - }; - } -} - -export function isTouchEvent(event) { - return ( - event.touches && event.touches.length || - event.changedTouches && event.changedTouches.length - ); -} - -export function getEdgeOffset(node, parent, offset = {top: 0, left: 0}) { - // Get the actual offsetTop / offsetLeft value, no matter how deep the node is nested - if (node) { - const nodeOffset = { - top: offset.top + node.offsetTop, - left: offset.left + node.offsetLeft, - }; - - if (node.parentNode !== parent) { - return getEdgeOffset(node.parentNode, parent, nodeOffset); - } else { - return nodeOffset; - } - } -} - - -export function getLockPixelOffset({lockOffset, width, height}) { - let offsetX = lockOffset; - let offsetY = lockOffset; - let unit = 'px'; - - if (typeof lockOffset === 'string') { - const match = /^[+-]?\d*(?:\.\d*)?(px|%)$/.exec(lockOffset); - - invariant( - match !== null, - 'lockOffset value should be a number or a string of a ' + - 'number followed by "px" or "%". Given %s', - lockOffset - ); - - offsetX = (offsetY = parseFloat(lockOffset)); - unit = match[1]; - } - - invariant( - isFinite(offsetX) && isFinite(offsetY), - 'lockOffset value should be a finite. Given %s', - lockOffset - ); - - if (unit === '%') { - offsetX = offsetX * width / 100; - offsetY = offsetY * height / 100; - } - - return { - x: offsetX, - y: offsetY, - }; -}