diff --git a/.gitignore b/.gitignore index d88833ad6..8da28fe6b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ dist styles.min.css styles.min.css.map coverage -npm-debug.log +npm-debug.log \ No newline at end of file diff --git a/README.md b/README.md index c970782f9..d5e14532d 100644 --- a/README.md +++ b/README.md @@ -1,186 +1,2 @@ # React Sortable (HOC) -> A set of higher-order components to turn any list into an animated, touch-friendly, sortable list. - -[![npm version](https://img.shields.io/npm/v/react-sortable-hoc.svg)](https://www.npmjs.com/package/react-sortable-hoc) -[![npm downloads](https://img.shields.io/npm/dm/react-sortable-hoc.svg)](https://www.npmjs.com/package/react-sortable-hoc) -[![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://github.com/clauderic/react-sortable-hoc/blob/master/LICENSE) -[![Gitter](https://badges.gitter.im/clauderic/react-sortable-hoc.svg)](https://gitter.im/clauderic/react-sortable-hoc) -![gzip size](http://img.badgesize.io/https://npmcdn.com/react-sortable-hoc/dist/umd/react-sortable-hoc.min.js?compression=gzip) - -### Examples available here: http://clauderic.github.io/react-sortable-hoc/ - -Features ---------------- -* **Higher Order Components** โ€“ Integrates with your existing components -* **Drag handle, auto-scrolling, locked axis, events, and more!** -* **Suuuper smooth animations** โ€“ Chasing the 60FPS dream ๐ŸŒˆ -* **Works with virtualization libraries: [react-virtualized](https://github.com/bvaughn/react-virtualized/), [react-tiny-virtual-list](https://github.com/clauderic/react-tiny-virtual-list), [react-infinite](https://github.com/seatgeek/react-infinite), etc.** -* **Horizontal lists, vertical lists, or a grid** โ†” โ†• โคก -* **Touch support** ๐Ÿ‘Œ - -Installation ------------- - -Using [npm](https://www.npmjs.com/package/react-sortable-hoc): - - $ npm install react-sortable-hoc --save - - -Then, using a module bundler that supports either CommonJS or ES2015 modules, such as [webpack](https://github.com/webpack/webpack): - -```js -// Using an ES6 transpiler like Babel -import {SortableContainer, SortableElement} from 'react-sortable-hoc'; - -// Not using an ES6 transpiler -var Sortable = require('react-sortable-hoc'); -var SortableContainer = Sortable.SortableContainer; -var SortableElement = Sortable.SortableElement; -``` - -Alternatively, an UMD build is also available: -```html - -``` - -Usage ------------- -### Basic Example - -```js -import React, {Component} from 'react'; -import {render} from 'react-dom'; -import {SortableContainer, SortableElement, arrayMove} from 'react-sortable-hoc'; - -const SortableItem = SortableElement(({value}) => -
  • {value}
  • -); - -const SortableList = SortableContainer(({items}) => { - return ( - - ); -}); - -class SortableComponent extends Component { - state = { - items: ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5', 'Item 6'], - }; - onSortEnd = ({oldIndex, newIndex}) => { - this.setState({ - items: arrayMove(this.state.items, oldIndex, newIndex), - }); - }; - render() { - return ; - } -} - -render(, document.getElementById('root')); -``` -That's it! React Sortable does not come with any styles by default, since it's meant to enhance your existing components. - -More code examples are available [here](https://github.com/clauderic/react-sortable-hoc/blob/master/examples/). - -Why should I use this? --------------------- -There are already a number of great Drag & Drop libraries out there (for instance, [react-dnd](https://github.com/gaearon/react-dnd/) is fantastic). If those libraries fit your needs, you should definitely give them a try first. However, most of those libraries rely on the HTML5 Drag & Drop API, which has some severe limitations. For instance, things rapidly become tricky if you need to support touch devices, if you need to lock dragging to an axis, or want to animate the nodes as they're being sorted. React Sortable HOC aims to provide a simple set of higher-order components to fill those gaps. If you're looking for a dead-simple, mobile-friendly way to add sortable functionality to your lists, then you're in the right place. - -### Prop Types - -#### SortableContainer HOC -| Property | Type | Default | Description | -|:---------------------------|:------------------|:-----------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| axis | String | `y` | Items can be sorted horizontally, vertically or in a grid. Possible values: `x`, `y` or `xy` | -| lockAxis | String | | If you'd like, you can lock movement to an axis while sorting. This is not something that is possible with HTML5 Drag & Drop. Possible values: `x` or `y`. | -| helperClass | String | | You can provide a class you'd like to add to the sortable helper to add some styles to it | -| transitionDuration | Number | `300` | The duration of the transition when elements shift positions. Set this to `0` if you'd like to disable transitions | -| pressDelay | Number | `0` | If you'd like elements to only become sortable after being pressed for a certain time, change this property. A good sensible default value for mobile is `200`. Cannot be used in conjunction with the `distance` prop. | -| pressThreshold | Number | `5` | Number of pixels of movement to tolerate before ignoring a press event. | -| distance | Number | `0` | If you'd like elements to only become sortable after being dragged a certain number of pixels. Cannot be used in conjunction with the `pressDelay` prop. | -| shouldCancelStart | Function | [Function](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableContainer/index.js#L48) | This function is invoked before sorting begins, and can be used to programatically cancel sorting before it begins. By default, it will cancel sorting if the event target is either an `input`, `textarea`, `select` or `option`. | -| onSortStart | Function | | Callback that is invoked when sorting begins. `function({node, index, collection}, event)` | -| onSortMove | Function | | Callback that is invoked during sorting as the cursor moves. `function(event)` | -| onSortOver | Function | | Callback that is invoked when moving over an item. `function({index, oldIndex, newIndex, collection}, e)` | -| onSortEnd | Function | | Callback that is invoked when sorting ends. `function({oldIndex, newIndex, collection}, e)` | -| useDragHandle | Boolean | `false` | If you're using the `SortableHandle` HOC, set this to `true` | -| useWindowAsScrollContainer | Boolean | `false` | If you want, you can set the `window` as the scrolling container | -| hideSortableGhost | Boolean | `true` | Whether to auto-hide the ghost element. By default, as a convenience, React Sortable List will automatically hide the element that is currently being sorted. Set this to false if you would like to apply your own styling. | -| lockToContainerEdges | Boolean | `false` | You can lock movement of the sortable element to it's parent `SortableContainer` | -| lockOffset | `OffsetValue`\* \ | [`OffsetValue`\*, `OffsetValue`\*] | `"50%"` | When `lockToContainerEdges` is set to `true`, this controls the offset distance between the sortable helper and the top/bottom edges of it's parent `SortableContainer`. Percentage values are relative to the height of the item currently being sorted. If you wish to specify different behaviours for locking to the *top* of the container vs the *bottom*, you may also pass in an `array` (For example: `["0%", "100%"]`). | -| getContainer | Function | | Optional function to return the scrollable container element. This property defaults to the `SortableContainer` element itself or (if `useWindowAsScrollContainer` is true) the window. Use this function to specify a custom container object (eg this is useful for integrating with certain 3rd party components such as `FlexTable`). This function is passed a single parameter (the `wrappedInstance` React element) and it is expected to return a DOM element. | -| getHelperDimensions | Function | [Function](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableContainer/index.js#L58) | Optional `function({node, index, collection})` that should return the computed dimensions of the SortableHelper. See [default implementation](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableContainer/index.js#L58) for more details | - -\* `OffsetValue` can either be a finite `Number` or a `String` made up of a number and a unit (`px` or `%`). -Examples: `10` (which is the same as `"10px"`), `"50%"` - -#### SortableElement HOC -| Property | Type | Default | Required? | Description | -|:-----------|:-----------------|:--------|:---------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| index | Number | | โœ“ | This is the element's sortableIndex within it's collection. This prop is required. | -| collection | Number or String | `0` | | The collection the element is part of. This is useful if you have multiple groups of sortable elements within the same `SortableContainer`. [Example](http://clauderic.github.io/react-sortable-hoc/#/basic-configuration/multiple-lists) | -| disabled | Boolean | `false` | | Whether the element should be sortable or not | - -FAQ ---------------- -### Running Examples - -In root folder: - -``` - $ npm install - $ npm run storybook -``` - -### Grid support -Need to sort items in a grid? We've got you covered! Just set the `axis` prop to `xy`. Grid support is currently limited to a setup where all the cells in the grid have the same width and height, though we're working hard to get variable width support in the near future. - -### Item disappearing when sorting / CSS issues -Upon sorting, `react-sortable-hoc` creates a clone of the element you are sorting (the _sortable-helper_) and appends it to the end of the `` tag. The original element will still be in-place to preserve its position in the DOM until the end of the drag (with inline-styling to make it invisible). If the _sortable-helper_ gets messed up from a CSS standpoint, consider that maybe your selectors to the draggable item are dependent on a parent element which isn't present anymore (again, since the _sortable-helper_ is at the end of the ``). This can also be a `z-index` issue, for example, when using `react-sortable-hoc` within a Bootstrap modal, you'll need to increase the `z-index` of the SortableHelper so it is displayed on top of the modal (see [#87](https://github.com/clauderic/react-sortable-hoc/issues/87) for more details). - -### Click events being swallowed -By default, `react-sortable-hoc` is triggered immediately on `mousedown`. If you'd like to prevent this behaviour, there are a number of strategies readily available. You can use the `distance` prop to set a minimum distance (in pixels) to be dragged before sorting is enabled. You can also use the `pressDelay` prop to add a delay before sorting is enabled. Alternatively, you can also use the [SortableHandle](https://github.com/clauderic/react-sortable-hoc/blob/master/src/SortableHandle/index.js) HOC. - -### Wrapper props not passed down to wrapped Component -All props for `SortableContainer` and `SortableElement` listed above are intentionally consumed by the wrapper component and are **not** passed down to the wrapped component. To make them available pass down the desired prop again with a different name. E.g.: - -```js -const SortableItem = SortableElement(({value, sortIndex}) => -
  • {value} - #{sortIndex}
  • -); - -const SortableList = SortableContainer(({items}) => { - return ( - - ); -}); -``` - -Dependencies ------------- -React Sortable HOC only depends on [invariant](https://github.com/zertosh/invariant). It has the following peerDependencies: `react`, `react-dom` - -Reporting Issues ----------------- -If believe you've found an issue, please [report it](https://github.com/clauderic/react-sortable-hoc/issues) along with any relevant details to reproduce it. The easiest way to do so is to fork this [jsfiddle](https://jsfiddle.net/clauderic/6r7r2cva/). - -Asking for help ----------------- -Please do not use the issue tracker for personal support requests. Instead, use [Gitter](https://gitter.im/clauderic/react-sortable-hoc) or StackOverflow. - -Contributions ------------- -Yes please! Feature requests / pull requests are welcome. +Fork of [react-sortable-hoc](https://github.com/clauderic/react-sortable-hoc) that includes this [pull request](https://github.com/clauderic/react-sortable-hoc/pull/194/commits/93cf2f911348e2b02b169d262aab12262362f8af) which allows the sortable-helper to be attached to the sortable-container instead of the `document.body`. diff --git a/dist/es6/Manager.js b/dist/es6/Manager.js new file mode 100644 index 000000000..57ab39d82 --- /dev/null +++ b/dist/es6/Manager.js @@ -0,0 +1,82 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require("babel-runtime/helpers/createClass"); + +var _createClass3 = _interopRequireDefault(_createClass2); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var Manager = function () { + function Manager() { + (0, _classCallCheck3.default)(this, Manager); + this.refs = {}; + } + + (0, _createClass3.default)(Manager, [{ + key: "add", + value: function add(collection, ref) { + if (!this.refs[collection]) { + this.refs[collection] = []; + } + + this.refs[collection].push(ref); + } + }, { + key: "remove", + value: function remove(collection, ref) { + var index = this.getIndex(collection, ref); + + if (index !== -1) { + this.refs[collection].splice(index, 1); + } + } + }, { + key: "isActive", + value: function isActive() { + return this.active; + } + }, { + key: "getActive", + value: function getActive() { + var _this = this; + + return this.refs[this.active.collection].find( + // eslint-disable-next-line eqeqeq + function (_ref) { + var node = _ref.node; + return node.sortableInfo.index == _this.active.index; + }); + } + }, { + key: "getIndex", + value: function getIndex(collection, ref) { + return this.refs[collection].indexOf(ref); + } + }, { + key: "getOrderedRefs", + value: function getOrderedRefs() { + var collection = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.active.collection; + + return this.refs[collection].sort(sortByIndex); + } + }]); + return Manager; +}(); + +exports.default = Manager; + + +function sortByIndex(_ref2, _ref3) { + var index1 = _ref2.node.sortableInfo.index; + var index2 = _ref3.node.sortableInfo.index; + + return index1 - index2; +} \ No newline at end of file diff --git a/dist/es6/SortableContainer/index.js b/dist/es6/SortableContainer/index.js new file mode 100644 index 000000000..70c8cb463 --- /dev/null +++ b/dist/es6/SortableContainer/index.js @@ -0,0 +1,780 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends2 = require('babel-runtime/helpers/extends'); + +var _extends3 = _interopRequireDefault(_extends2); + +var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); + +var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); + +var _promise = require('babel-runtime/core-js/promise'); + +var _promise2 = _interopRequireDefault(_promise); + +var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); + +var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +exports.default = sortableContainer; + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _propTypes = require('prop-types'); + +var _propTypes2 = _interopRequireDefault(_propTypes); + +var _reactDom = require('react-dom'); + +var _invariant = require('invariant'); + +var _invariant2 = _interopRequireDefault(_invariant); + +var _Manager = require('../Manager'); + +var _Manager2 = _interopRequireDefault(_Manager); + +var _utils = require('../utils'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// Export Higher Order Sortable Container Component +function sortableContainer(WrappedComponent) { + var _class, _temp; + + var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { withRef: false }; + + return _temp = _class = function (_Component) { + (0, _inherits3.default)(_class, _Component); + + function _class(props) { + (0, _classCallCheck3.default)(this, _class); + + var _this = (0, _possibleConstructorReturn3.default)(this, (_class.__proto__ || (0, _getPrototypeOf2.default)(_class)).call(this, props)); + + _this.handleStart = function (event) { + var _this$props = _this.props, + distance = _this$props.distance, + shouldCancelStart = _this$props.shouldCancelStart; + + + if (event.button === 2 || shouldCancelStart(event)) { + return false; + } + + _this._touched = true; + _this._pos = (0, _utils.getPosition)(event); + + var node = (0, _utils.closest)(event.target, function (el) { + return el.sortableInfo != null; + }); + + if (node && node.sortableInfo && _this.nodeIsChild(node) && !_this.state.sorting) { + var useDragHandle = _this.props.useDragHandle; + var _node$sortableInfo = node.sortableInfo, + index = _node$sortableInfo.index, + collection = _node$sortableInfo.collection; + + + if (useDragHandle && !(0, _utils.closest)(event.target, function (el) { + return el.sortableHandle != null; + })) return; + + _this.manager.active = { index: index, collection: collection }; + + /* + * 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 (!(0, _utils.isTouchEvent)(event) && event.target.tagName.toLowerCase() === 'a') { + event.preventDefault(); + } + + if (!distance) { + if (_this.props.pressDelay === 0) { + _this.handlePress(event); + } else { + _this.pressTimer = setTimeout(function () { + return _this.handlePress(event); + }, _this.props.pressDelay); + } + } + } + }; + + _this.nodeIsChild = function (node) { + return node.sortableInfo.manager === _this.manager; + }; + + _this.handleMove = function (event) { + var _this$props2 = _this.props, + distance = _this$props2.distance, + pressThreshold = _this$props2.pressThreshold; + + + if (!_this.state.sorting && _this._touched) { + var position = (0, _utils.getPosition)(event); + var delta = _this._delta = { + x: _this._pos.x - position.x, + y: _this._pos.y - position.y + }; + var 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); + } + } + }; + + _this.handleEnd = function () { + var distance = _this.props.distance; + + + _this._touched = false; + + if (!distance) { + _this.cancel(); + } + }; + + _this.cancel = function () { + if (!_this.state.sorting) { + clearTimeout(_this.pressTimer); + _this.manager.active = null; + } + }; + + _this.handlePress = function (event) { + var active = _this.manager.getActive(); + + if (active) { + var _this$props3 = _this.props, + axis = _this$props3.axis, + getHelperDimensions = _this$props3.getHelperDimensions, + helperClass = _this$props3.helperClass, + hideSortableGhost = _this$props3.hideSortableGhost, + onSortStart = _this$props3.onSortStart, + useWindowAsScrollContainer = _this$props3.useWindowAsScrollContainer, + useContainerAsSortableHelperParent = _this$props3.useContainerAsSortableHelperParent; + var node = active.node, + collection = active.collection; + var index = node.sortableInfo.index; + + var margin = (0, _utils.getElementMargin)(node); + + var containerBoundingRect = _this.container.getBoundingClientRect(); + var dimensions = getHelperDimensions({ index: index, node: node, collection: collection }); + + _this.node = node; + _this.margin = margin; + _this.width = dimensions.width; + _this.height = dimensions.height; + _this.marginOffset = { + x: _this.margin.left + _this.margin.right, + y: Math.max(_this.margin.top, _this.margin.bottom) + }; + _this.boundingClientRect = node.getBoundingClientRect(); + _this.containerBoundingRect = containerBoundingRect; + _this.index = index; + _this.newIndex = index; + + _this.axis = { + x: axis.indexOf('x') >= 0, + y: axis.indexOf('y') >= 0 + }; + _this.offsetEdge = (0, _utils.getEdgeOffset)(node, _this.container); + _this.initialOffset = (0, _utils.getPosition)(event); + _this.initialScroll = { + top: _this.container.scrollTop, + left: _this.container.scrollLeft + }; + + _this.initialWindowScroll = { + top: window.pageYOffset, + left: window.pageXOffset + }; + + var fields = node.querySelectorAll('input, textarea, select'); + var clonedNode = node.cloneNode(true); + var clonedFields = [].concat((0, _toConsumableArray3.default)(clonedNode.querySelectorAll('input, textarea, select'))); // Convert NodeList to Array + + clonedFields.forEach(function (field, index) { + if (field.type !== 'file' && fields[index]) { + field.value = fields[index].value; + } + }); + + var helperParent = useContainerAsSortableHelperParent ? _this.container : _this.document.body; + _this.helper = helperParent.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; + } + + _this.minTranslate = {}; + _this.maxTranslate = {}; + if (_this.axis.x) { + _this.minTranslate.x = (useWindowAsScrollContainer ? 0 : containerBoundingRect.left) - _this.boundingClientRect.left - _this.width / 2; + _this.maxTranslate.x = (useWindowAsScrollContainer ? _this.contentWindow.innerWidth : containerBoundingRect.left + containerBoundingRect.width) - _this.boundingClientRect.left - _this.width / 2; + } + if (_this.axis.y) { + _this.minTranslate.y = (useWindowAsScrollContainer ? 0 : containerBoundingRect.top) - _this.boundingClientRect.top - _this.height / 2; + _this.maxTranslate.y = (useWindowAsScrollContainer ? _this.contentWindow.innerHeight : containerBoundingRect.top + containerBoundingRect.height) - _this.boundingClientRect.top - _this.height / 2; + } + + if (helperClass) { + var _this$helper$classLis; + + (_this$helper$classLis = _this.helper.classList).add.apply(_this$helper$classLis, (0, _toConsumableArray3.default)(helperClass.split(' '))); + } + + _this.listenerNode = event.touches ? node : _this.contentWindow; + _utils.events.move.forEach(function (eventName) { + return _this.listenerNode.addEventListener(eventName, _this.handleSortMove, false); + }); + _utils.events.end.forEach(function (eventName) { + return _this.listenerNode.addEventListener(eventName, _this.handleSortEnd, false); + }); + + _this.setState({ + sorting: true, + sortingIndex: index + }); + + if (onSortStart) { + onSortStart({ node: node, index: index, collection: collection }, event); + } + } + }; + + _this.handleSortMove = function (event) { + var onSortMove = _this.props.onSortMove; + + event.preventDefault(); // Prevent scrolling on mobile + + _this.updatePosition(event); + _this.animateNodes(); + _this.autoscroll(); + + if (onSortMove) { + onSortMove(event); + } + }; + + _this.handleSortEnd = function (event) { + var _this$props4 = _this.props, + hideSortableGhost = _this$props4.hideSortableGhost, + onSortEnd = _this$props4.onSortEnd; + var collection = _this.manager.active.collection; + + // Remove the event listeners if the node is still in the DOM + + if (_this.listenerNode) { + _utils.events.move.forEach(function (eventName) { + return _this.listenerNode.removeEventListener(eventName, _this.handleSortMove); + }); + _utils.events.end.forEach(function (eventName) { + return _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 = ''; + } + + var nodes = _this.manager.refs[collection]; + for (var i = 0, len = nodes.length; i < len; i++) { + var node = nodes[i]; + var el = node.node; + + // Clear the cached offsetTop / offsetLeft value + node.edgeOffset = null; + + // Remove the transforms / transitions + el.style[_utils.vendorPrefix + 'Transform'] = ''; + el.style[_utils.vendorPrefix + 'TransitionDuration'] = ''; + } + + // Stop autoscroll + clearInterval(_this.autoscrollInterval); + _this.autoscrollInterval = null; + + // Update state + _this.manager.active = null; + + _this.setState({ + sorting: false, + sortingIndex: null + }); + + if (typeof onSortEnd === 'function') { + onSortEnd({ + oldIndex: _this.index, + newIndex: _this.newIndex, + collection: collection + }, event); + } + + _this._touched = false; + }; + + _this.autoscroll = function () { + var translate = _this.translate; + var direction = { + x: 0, + y: 0 + }; + var speed = { + x: 1, + y: 1 + }; + var acceleration = { + x: 10, + y: 10 + }; + + if (translate.y >= _this.maxTranslate.y - _this.height / 2) { + direction.y = 1; // Scroll Down + speed.y = acceleration.y * Math.abs((_this.maxTranslate.y - _this.height / 2 - translate.y) / _this.height); + } else if (translate.x >= _this.maxTranslate.x - _this.width / 2) { + direction.x = 1; // Scroll Right + speed.x = acceleration.x * Math.abs((_this.maxTranslate.x - _this.width / 2 - translate.x) / _this.width); + } else if (translate.y <= _this.minTranslate.y + _this.height / 2) { + direction.y = -1; // Scroll Up + speed.y = acceleration.y * Math.abs((translate.y - _this.height / 2 - _this.minTranslate.y) / _this.height); + } else if (translate.x <= _this.minTranslate.x + _this.width / 2) { + direction.x = -1; // Scroll Left + speed.x = acceleration.x * Math.abs((translate.x - _this.width / 2 - _this.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(function () { + _this.isAutoScrolling = true; + var offset = { + left: 1 * speed.x * direction.x, + top: 1 * speed.y * direction.y + }; + _this.scrollContainer.scrollTop += offset.top; + _this.scrollContainer.scrollLeft += offset.left; + _this.translate.x += offset.left; + _this.translate.y += offset.top; + _this.animateNodes(); + }, 5); + } + }; + + _this.manager = new _Manager2.default(); + _this.events = { + start: _this.handleStart, + move: _this.handleMove, + end: _this.handleEnd + }; + + (0, _invariant2.default)(!(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 = {}; + return _this; + } + + (0, _createClass3.default)(_class, [{ + key: 'getChildContext', + value: function getChildContext() { + return { + manager: this.manager + }; + } + }, { + key: 'componentDidMount', + value: function componentDidMount() { + var _this2 = this; + + var useWindowAsScrollContainer = this.props.useWindowAsScrollContainer; + + /* + * 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 + */ + + var container = this.getContainer(); + + _promise2.default.resolve(container).then(function (containerNode) { + _this2.container = containerNode; + _this2.document = _this2.container.ownerDocument || document; + + var contentWindow = _this2.props.contentWindow || _this2.document.defaultView || window; + + _this2.contentWindow = typeof contentWindow === 'function' ? contentWindow() : contentWindow; + _this2.scrollContainer = useWindowAsScrollContainer ? _this2.document.scrollingElement || _this2.document.documentElement : _this2.container; + + var _loop = function _loop(key) { + if (_this2.events.hasOwnProperty(key)) { + _utils.events[key].forEach(function (eventName) { + return _this2.container.addEventListener(eventName, _this2.events[key], false); + }); + } + }; + + for (var key in _this2.events) { + _loop(key); + } + }); + } + }, { + key: 'componentWillUnmount', + value: function componentWillUnmount() { + var _this3 = this; + + if (this.container) { + var _loop2 = function _loop2(key) { + if (_this3.events.hasOwnProperty(key)) { + _utils.events[key].forEach(function (eventName) { + return _this3.container.removeEventListener(eventName, _this3.events[key]); + }); + } + }; + + for (var key in this.events) { + _loop2(key); + } + } + } + }, { + key: 'getLockPixelOffsets', + value: function getLockPixelOffsets() { + var width = this.width, + height = this.height; + var lockOffset = this.props.lockOffset; + + var offsets = Array.isArray(lockOffset) ? lockOffset : [lockOffset, lockOffset]; + + (0, _invariant2.default)(offsets.length === 2, 'lockOffset prop of SortableContainer should be a single ' + 'value or an array of exactly two values. Given %s', lockOffset); + + var _offsets = (0, _slicedToArray3.default)(offsets, 2), + minLockOffset = _offsets[0], + maxLockOffset = _offsets[1]; + + return [(0, _utils.getLockPixelOffset)({ lockOffset: minLockOffset, width: width, height: height }), (0, _utils.getLockPixelOffset)({ lockOffset: maxLockOffset, width: width, height: height })]; + } + }, { + key: 'updatePosition', + value: function updatePosition(event) { + var _props = this.props, + lockAxis = _props.lockAxis, + lockToContainerEdges = _props.lockToContainerEdges; + + + var offset = (0, _utils.getPosition)(event); + var 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) { + var _getLockPixelOffsets = this.getLockPixelOffsets(), + _getLockPixelOffsets2 = (0, _slicedToArray3.default)(_getLockPixelOffsets, 2), + minLockOffset = _getLockPixelOffsets2[0], + maxLockOffset = _getLockPixelOffsets2[1]; + + var minOffset = { + x: this.width / 2 - minLockOffset.x, + y: this.height / 2 - minLockOffset.y + }; + var maxOffset = { + x: this.width / 2 - maxLockOffset.x, + y: this.height / 2 - maxLockOffset.y + }; + + translate.x = (0, _utils.limit)(this.minTranslate.x + minOffset.x, this.maxTranslate.x - maxOffset.x, translate.x); + translate.y = (0, _utils.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[_utils.vendorPrefix + 'Transform'] = 'translate3d(' + translate.x + 'px,' + translate.y + 'px, 0)'; + } + }, { + key: 'animateNodes', + value: function animateNodes() { + var _props2 = this.props, + transitionDuration = _props2.transitionDuration, + hideSortableGhost = _props2.hideSortableGhost, + onSortOver = _props2.onSortOver; + + var nodes = this.manager.getOrderedRefs(); + var containerScrollDelta = { + left: this.container.scrollLeft - this.initialScroll.left, + top: this.container.scrollTop - this.initialScroll.top + }; + var sortingOffset = { + left: this.offsetEdge.left + this.translate.x + containerScrollDelta.left, + top: this.offsetEdge.top + this.translate.y + containerScrollDelta.top + }; + var windowScrollDelta = { + top: window.pageYOffset - this.initialWindowScroll.top, + left: window.pageXOffset - this.initialWindowScroll.left + }; + var prevIndex = this.newIndex; + this.newIndex = null; + + for (var i = 0, len = nodes.length; i < len; i++) { + var node = nodes[i].node; + + var index = node.sortableInfo.index; + var width = node.offsetWidth; + var height = node.offsetHeight; + var offset = { + width: this.width > width ? width / 2 : this.width / 2, + height: this.height > height ? height / 2 : this.height / 2 + }; + + var translate = { + x: 0, + y: 0 + }; + var edgeOffset = nodes[i].edgeOffset; + + // If we haven't cached the node's offsetTop / offsetLeft value + + if (!edgeOffset) { + nodes[i].edgeOffset = edgeOffset = (0, _utils.getEdgeOffset)(node, this.container); + } + + // Get a reference to the next and previous node + var nextNode = i < nodes.length - 1 && nodes[i + 1]; + var 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 = (0, _utils.getEdgeOffset)(nextNode.node, this.container); + } + + // If the node is the one we're currently animating, skip it + if (index === this.index) { + 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. + */ + this.sortableGhost = node; + node.style.visibility = 'hidden'; + node.style.opacity = 0; + } + continue; + } + + if (transitionDuration) { + node.style[_utils.vendorPrefix + 'TransitionDuration'] = transitionDuration + 'ms'; + } + + if (this.axis.x) { + if (this.axis.y) { + // Calculations for a grid setup + if (index < this.index && (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 > this.index && (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 > this.index && sortingOffset.left + windowScrollDelta.left + offset.width >= edgeOffset.left) { + translate.x = -(this.width + this.marginOffset.x); + this.newIndex = index; + } else if (index < this.index && 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 > this.index && sortingOffset.top + windowScrollDelta.top + offset.height >= edgeOffset.top) { + translate.y = -(this.height + this.marginOffset.y); + this.newIndex = index; + } else if (index < this.index && sortingOffset.top + windowScrollDelta.top <= edgeOffset.top + offset.height) { + translate.y = this.height + this.marginOffset.y; + if (this.newIndex == null) { + this.newIndex = index; + } + } + } + node.style[_utils.vendorPrefix + 'Transform'] = 'translate3d(' + translate.x + 'px,' + translate.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, + collection: this.manager.active.collection + }); + } + } + }, { + key: 'getWrappedInstance', + value: function getWrappedInstance() { + (0, _invariant2.default)(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; + } + }, { + key: 'getContainer', + value: function getContainer() { + var getContainer = this.props.getContainer; + + + if (typeof getContainer !== 'function') { + return (0, _reactDom.findDOMNode)(this); + } + + return getContainer(config.withRef ? this.getWrappedInstance() : undefined); + } + }, { + key: 'render', + value: function render() { + var ref = config.withRef ? 'wrappedInstance' : null; + + return _react2.default.createElement(WrappedComponent, (0, _extends3.default)({ + ref: ref + }, (0, _utils.omit)(this.props, 'contentWindow', 'useWindowAsScrollContainer', 'distance', 'helperClass', 'hideSortableGhost', 'transitionDuration', 'useDragHandle', 'pressDelay', 'pressThreshold', 'shouldCancelStart', 'onSortStart', 'onSortMove', 'onSortEnd', 'axis', 'lockAxis', 'lockOffset', 'lockToContainerEdges', 'getContainer', 'getHelperDimensions'))); + } + }]); + return _class; + }(_react.Component), _class.displayName = (0, _utils.provideDisplayName)('sortableList', WrappedComponent), _class.defaultProps = { + axis: 'y', + transitionDuration: 300, + pressDelay: 0, + pressThreshold: 5, + distance: 0, + useWindowAsScrollContainer: false, + useContainerAsSortableHelperParent: false, + hideSortableGhost: true, + shouldCancelStart: function shouldCancelStart(e) { + // Cancel sorting if the event target is an `input`, `textarea`, `select` or `option` + var 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: function getHelperDimensions(_ref) { + var node = _ref.node; + return { + width: node.offsetWidth, + height: node.offsetHeight + }; + } + }, _class.propTypes = { + axis: _propTypes2.default.oneOf(['x', 'y', 'xy']), + distance: _propTypes2.default.number, + lockAxis: _propTypes2.default.string, + helperClass: _propTypes2.default.string, + transitionDuration: _propTypes2.default.number, + contentWindow: _propTypes2.default.any, + onSortStart: _propTypes2.default.func, + onSortMove: _propTypes2.default.func, + onSortOver: _propTypes2.default.func, + onSortEnd: _propTypes2.default.func, + shouldCancelStart: _propTypes2.default.func, + pressDelay: _propTypes2.default.number, + useDragHandle: _propTypes2.default.bool, + useWindowAsScrollContainer: _propTypes2.default.bool, + useContainerAsSortableHelperParent: _propTypes2.default.bool, + hideSortableGhost: _propTypes2.default.bool, + lockToContainerEdges: _propTypes2.default.bool, + lockOffset: _propTypes2.default.oneOfType([_propTypes2.default.number, _propTypes2.default.string, _propTypes2.default.arrayOf(_propTypes2.default.oneOfType([_propTypes2.default.number, _propTypes2.default.string]))]), + getContainer: _propTypes2.default.func, + getHelperDimensions: _propTypes2.default.func + }, _class.childContextTypes = { + manager: _propTypes2.default.object.isRequired + }, _temp; +} \ No newline at end of file diff --git a/dist/es6/SortableElement/index.js b/dist/es6/SortableElement/index.js new file mode 100644 index 000000000..f83880872 --- /dev/null +++ b/dist/es6/SortableElement/index.js @@ -0,0 +1,154 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends2 = require('babel-runtime/helpers/extends'); + +var _extends3 = _interopRequireDefault(_extends2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +exports.default = sortableElement; + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _propTypes = require('prop-types'); + +var _propTypes2 = _interopRequireDefault(_propTypes); + +var _reactDom = require('react-dom'); + +var _invariant = require('invariant'); + +var _invariant2 = _interopRequireDefault(_invariant); + +var _utils = require('../utils'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// Export Higher Order Sortable Element Component +function sortableElement(WrappedComponent) { + var _class, _temp; + + var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { withRef: false }; + + return _temp = _class = function (_Component) { + (0, _inherits3.default)(_class, _Component); + + function _class() { + (0, _classCallCheck3.default)(this, _class); + return (0, _possibleConstructorReturn3.default)(this, (_class.__proto__ || (0, _getPrototypeOf2.default)(_class)).apply(this, arguments)); + } + + (0, _createClass3.default)(_class, [{ + key: 'componentDidMount', + value: function componentDidMount() { + var _props = this.props, + collection = _props.collection, + disabled = _props.disabled, + index = _props.index; + + + if (!disabled) { + this.setDraggable(collection, index); + } + } + }, { + key: 'componentWillReceiveProps', + value: function componentWillReceiveProps(nextProps) { + if (this.props.index !== nextProps.index && this.node) { + this.node.sortableInfo.index = nextProps.index; + } + if (this.props.disabled !== nextProps.disabled) { + var collection = nextProps.collection, + disabled = nextProps.disabled, + index = nextProps.index; + + 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); + } + } + }, { + key: 'componentWillUnmount', + value: function componentWillUnmount() { + var _props2 = this.props, + collection = _props2.collection, + disabled = _props2.disabled; + + + if (!disabled) this.removeDraggable(collection); + } + }, { + key: 'setDraggable', + value: function setDraggable(collection, index) { + var node = this.node = (0, _reactDom.findDOMNode)(this); + + node.sortableInfo = { + index: index, + collection: collection, + manager: this.context.manager + }; + + this.ref = { node: node }; + this.context.manager.add(collection, this.ref); + } + }, { + key: 'removeDraggable', + value: function removeDraggable(collection) { + this.context.manager.remove(collection, this.ref); + } + }, { + key: 'getWrappedInstance', + value: function getWrappedInstance() { + (0, _invariant2.default)(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; + } + }, { + key: 'render', + value: function render() { + var ref = config.withRef ? 'wrappedInstance' : null; + + return _react2.default.createElement(WrappedComponent, (0, _extends3.default)({ + ref: ref + }, (0, _utils.omit)(this.props, 'collection', 'disabled', 'index'))); + } + }]); + return _class; + }(_react.Component), _class.displayName = (0, _utils.provideDisplayName)('sortableElement', WrappedComponent), _class.contextTypes = { + manager: _propTypes2.default.object.isRequired + }, _class.propTypes = { + index: _propTypes2.default.number.isRequired, + collection: _propTypes2.default.oneOfType([_propTypes2.default.number, _propTypes2.default.string]), + disabled: _propTypes2.default.bool + }, _class.defaultProps = { + collection: 0 + }, _temp; +} \ No newline at end of file diff --git a/dist/es6/SortableHandle/index.js b/dist/es6/SortableHandle/index.js new file mode 100644 index 000000000..805703418 --- /dev/null +++ b/dist/es6/SortableHandle/index.js @@ -0,0 +1,83 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _extends2 = require('babel-runtime/helpers/extends'); + +var _extends3 = _interopRequireDefault(_extends2); + +var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); + +var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); + +var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); + +var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); + +var _createClass2 = require('babel-runtime/helpers/createClass'); + +var _createClass3 = _interopRequireDefault(_createClass2); + +var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); + +var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); + +var _inherits2 = require('babel-runtime/helpers/inherits'); + +var _inherits3 = _interopRequireDefault(_inherits2); + +exports.default = sortableHandle; + +var _react = require('react'); + +var _react2 = _interopRequireDefault(_react); + +var _reactDom = require('react-dom'); + +var _invariant = require('invariant'); + +var _invariant2 = _interopRequireDefault(_invariant); + +var _utils = require('../utils'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// Export Higher Order Sortable Element Component +function sortableHandle(WrappedComponent) { + var _class, _temp; + + var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : { withRef: false }; + + return _temp = _class = function (_Component) { + (0, _inherits3.default)(_class, _Component); + + function _class() { + (0, _classCallCheck3.default)(this, _class); + return (0, _possibleConstructorReturn3.default)(this, (_class.__proto__ || (0, _getPrototypeOf2.default)(_class)).apply(this, arguments)); + } + + (0, _createClass3.default)(_class, [{ + key: 'componentDidMount', + value: function componentDidMount() { + var node = (0, _reactDom.findDOMNode)(this); + node.sortableHandle = true; + } + }, { + key: 'getWrappedInstance', + value: function getWrappedInstance() { + (0, _invariant2.default)(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; + } + }, { + key: 'render', + value: function render() { + var ref = config.withRef ? 'wrappedInstance' : null; + + return _react2.default.createElement(WrappedComponent, (0, _extends3.default)({ ref: ref }, this.props)); + } + }]); + return _class; + }(_react.Component), _class.displayName = (0, _utils.provideDisplayName)('sortableHandle', WrappedComponent), _temp; +} \ No newline at end of file diff --git a/dist/es6/index.js b/dist/es6/index.js new file mode 100644 index 000000000..684c92861 --- /dev/null +++ b/dist/es6/index.js @@ -0,0 +1,36 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.arrayMove = exports.sortableHandle = exports.sortableElement = exports.sortableContainer = exports.SortableHandle = exports.SortableElement = exports.SortableContainer = undefined; + +var _utils = require('./utils'); + +Object.defineProperty(exports, 'arrayMove', { + enumerable: true, + get: function get() { + return _utils.arrayMove; + } +}); + +var _SortableContainer2 = require('./SortableContainer'); + +var _SortableContainer3 = _interopRequireDefault(_SortableContainer2); + +var _SortableElement2 = require('./SortableElement'); + +var _SortableElement3 = _interopRequireDefault(_SortableElement2); + +var _SortableHandle2 = require('./SortableHandle'); + +var _SortableHandle3 = _interopRequireDefault(_SortableHandle2); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.SortableContainer = _SortableContainer3.default; +exports.SortableElement = _SortableElement3.default; +exports.SortableHandle = _SortableHandle3.default; +exports.sortableContainer = _SortableContainer3.default; +exports.sortableElement = _SortableElement3.default; +exports.sortableHandle = _SortableHandle3.default; \ No newline at end of file diff --git a/dist/es6/utils.js b/dist/es6/utils.js new file mode 100644 index 000000000..89eb182ab --- /dev/null +++ b/dist/es6/utils.js @@ -0,0 +1,186 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.vendorPrefix = exports.events = undefined; + +var _keys = require('babel-runtime/core-js/object/keys'); + +var _keys2 = _interopRequireDefault(_keys); + +exports.arrayMove = arrayMove; +exports.omit = omit; +exports.closest = closest; +exports.limit = limit; +exports.getElementMargin = getElementMargin; +exports.provideDisplayName = provideDisplayName; +exports.getPosition = getPosition; +exports.isTouchEvent = isTouchEvent; +exports.getEdgeOffset = getEdgeOffset; +exports.getLockPixelOffset = getLockPixelOffset; + +var _invariant = require('invariant'); + +var _invariant2 = _interopRequireDefault(_invariant); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function arrayMove(arr, previousIndex, newIndex) { + var array = arr.slice(0); + if (newIndex >= array.length) { + var k = newIndex - array.length; + while (k-- + 1) { + array.push(undefined); + } + } + array.splice(newIndex, 0, array.splice(previousIndex, 1)[0]); + return array; +} + +function omit(obj) { + for (var _len = arguments.length, keysToOmit = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + keysToOmit[_key - 1] = arguments[_key]; + } + + return (0, _keys2.default)(obj).reduce(function (acc, key) { + if (keysToOmit.indexOf(key) === -1) acc[key] = obj[key]; + return acc; + }, {}); +} + +var events = exports.events = { + start: ['touchstart', 'mousedown'], + move: ['touchmove', 'mousemove'], + end: ['touchend', 'touchcancel', 'mouseup'] +}; + +var vendorPrefix = exports.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. + var styles = window.getComputedStyle(document.documentElement, '') || ['-moz-hidden-iframe']; + var 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) : ''; + } +}(); + +function closest(el, fn) { + while (el) { + if (fn(el)) return el; + el = el.parentNode; + } +} + +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; +} + +function getElementMargin(element) { + var style = window.getComputedStyle(element); + + return { + top: getCSSPixelValue(style.marginTop), + right: getCSSPixelValue(style.marginRight), + bottom: getCSSPixelValue(style.marginBottom), + left: getCSSPixelValue(style.marginLeft) + }; +} + +function provideDisplayName(prefix, Component) { + var componentName = Component.displayName || Component.name; + + return componentName ? prefix + '(' + componentName + ')' : prefix; +} + +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 + }; + } +} + +function isTouchEvent(event) { + return event.touches && event.touches.length || event.changedTouches && event.changedTouches.length; +} + +function getEdgeOffset(node, parent) { + var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : { top: 0, left: 0 }; + + // Get the actual offsetTop / offsetLeft value, no matter how deep the node is nested + if (node) { + var nodeOffset = { + top: offset.top + node.offsetTop, + left: offset.left + node.offsetLeft + }; + + if (node.parentNode !== parent) { + return getEdgeOffset(node.parentNode, parent, nodeOffset); + } else { + return nodeOffset; + } + } +} + +function getLockPixelOffset(_ref) { + var lockOffset = _ref.lockOffset, + width = _ref.width, + height = _ref.height; + + var offsetX = lockOffset; + var offsetY = lockOffset; + var unit = 'px'; + + if (typeof lockOffset === 'string') { + var match = /^[+-]?\d*(?:\.\d*)?(px|%)$/.exec(lockOffset); + + (0, _invariant2.default)(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]; + } + + (0, _invariant2.default)(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 + }; +} \ No newline at end of file diff --git a/package.json b/package.json index 94a34ce77..ef4ec73ec 100644 --- a/package.json +++ b/package.json @@ -1,23 +1,20 @@ { - "name": "react-sortable-hoc", - "version": "0.8.3", - "description": "Set of higher-order components to turn any list into a sortable, touch-friendly, animated list", - "author": { - "name": "Clauderic Demers", - "email": "me@ced.io" - }, - "user": "clauderic", - "homepage": "https://github.com/clauderic/react-sortable-hoc", + "name": "@colombo.lawrence/react-sortable-hoc-private", + "version": "0.1.0", + "description": "Set of higher-order components to turn any list into a sortable, touch-friendly, animated list. Forked to allow the sortable-helper to be appended to the sortable-container", + "author": "Lawrence Colombo ", + "user": "colombo.lawrence", + "homepage": "https://github.com/LawrenceColombo/react-sortable-hoc", "main": "dist/commonjs/index.js", "jsnext:main": "dist/es6/index.js", "types": "types/index.d.ts", "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/clauderic/react-sortable-hoc.git" + "url": "git+https://github.com/LawrenceColombo/react-sortable-hoc.git" }, "bugs": { - "url": "https://github.com/clauderic/react-sortable-hoc/issues" + "url": "https://github.com/LawrenceColombo/react-sortable-hoc/issues" }, "keywords": [ "react", diff --git a/src/SortableContainer/index.js b/src/SortableContainer/index.js index 0a1706ea8..fbdddf636 100644 --- a/src/SortableContainer/index.js +++ b/src/SortableContainer/index.js @@ -47,6 +47,7 @@ export default function sortableContainer(WrappedComponent, config = {withRef: f pressThreshold: 5, distance: 0, useWindowAsScrollContainer: false, + useContainerAsSortableHelperParent: false, hideSortableGhost: true, shouldCancelStart: function(e) { // Cancel sorting if the event target is an `input`, `textarea`, `select` or `option` @@ -79,6 +80,7 @@ export default function sortableContainer(WrappedComponent, config = {withRef: f pressDelay: PropTypes.number, useDragHandle: PropTypes.bool, useWindowAsScrollContainer: PropTypes.bool, + useContainerAsSortableHelperParent: PropTypes.bool, hideSortableGhost: PropTypes.bool, lockToContainerEdges: PropTypes.bool, lockOffset: PropTypes.oneOfType([ @@ -252,6 +254,7 @@ export default function sortableContainer(WrappedComponent, config = {withRef: f hideSortableGhost, onSortStart, useWindowAsScrollContainer, + useContainerAsSortableHelperParent, } = this.props; const {node, collection} = active; const {index} = node.sortableInfo; @@ -301,7 +304,8 @@ export default function sortableContainer(WrappedComponent, config = {withRef: f } }); - this.helper = this.document.body.appendChild(clonedNode); + const helperParent = useContainerAsSortableHelperParent ? this.container : this.document.body; + this.helper = helperParent.appendChild(clonedNode); this.helper.style.position = 'fixed'; this.helper.style.top = `${this.boundingClientRect.top - margin.top}px`;