lucid-ui
Version:
A UI component library from AppNexus.
198 lines (177 loc) • 7.68 kB
JavaScript
import _keys from "lodash/keys";
import _map from "lodash/map";
import _isFinite from "lodash/isFinite";
import _noop from "lodash/noop";
import _isNumber from "lodash/isNumber";
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
import React, { useRef } from 'react';
import PropTypes from 'react-peek/prop-types';
import { lucidClassNames } from '../../util/style-helpers';
import { buildModernHybridComponent } from '../../util/state-management';
import DotsIcon from '../Icon/DotsIcon/DotsIcon';
import * as reducers from './DraggableList.reducers';
import { findTypes, omitProps } from '../../util/component-types';
var cx = lucidClassNames.bind('&-DraggableList');
var bool = PropTypes.bool,
func = PropTypes.func,
object = PropTypes.object,
number = PropTypes.number,
string = PropTypes.string;
var DraggableListItem = function DraggableListItem(_props) {
return null;
};
DraggableListItem.displayName = 'DraggableList.Item';
DraggableListItem.peek = {
description: "\n\tRenders a `<div>` that acts as an item in the list\n\t"
};
DraggableListItem.propName = 'Item';
DraggableListItem.propTypes = {
children: PropTypes.node
};
/** Verifies its ok to drop an item given the current drag indexes and
* provides a typeguard that dragIndex and dragOverIndex aren't undefined
*/
var isValidDropIndex = function isValidDropIndex(dragIndexes) {
var dragOverIndex = dragIndexes.dragOverIndex,
dragIndex = dragIndexes.dragIndex;
return _isNumber(dragOverIndex) && _isNumber(dragIndex) && (dragOverIndex < dragIndex || dragOverIndex > dragIndex + 1);
};
var DraggableList = function DraggableList(props) {
var style = props.style,
className = props.className,
dragIndex = props.dragIndex,
dragOverIndex = props.dragOverIndex,
_props$hasDragHandle = props.hasDragHandle,
hasDragHandle = _props$hasDragHandle === void 0 ? true : _props$hasDragHandle,
_props$onDragStart = props.onDragStart,
onDragStart = _props$onDragStart === void 0 ? _noop : _props$onDragStart,
_props$onDragEnd = props.onDragEnd,
onDragEnd = _props$onDragEnd === void 0 ? _noop : _props$onDragEnd,
_props$onDragOver = props.onDragOver,
onDragOver = _props$onDragOver === void 0 ? _noop : _props$onDragOver,
_props$onDrop = props.onDrop,
onDrop = _props$onDrop === void 0 ? _noop : _props$onDrop,
passThroughs = _objectWithoutProperties(props, ["style", "className", "dragIndex", "dragOverIndex", "hasDragHandle", "onDragStart", "onDragEnd", "onDragOver", "onDrop"]);
var lastItemEl = useRef(null); //This object helps handle 'undefined' indexes in a way that makes typescript happy
var dragIndexes = {
dragIndex: dragIndex,
dragOverIndex: dragOverIndex
};
var handleDragStart = function handleDragStart(index) {
return function (event) {
var dataTransfer = event.dataTransfer;
dataTransfer.effectAllowed = 'move';
dataTransfer.dropEffect = 'move';
dataTransfer.setData('drag', 'drag');
onDragStart(index, {
event: event,
props: props
});
};
};
var handleDragEnd = function handleDragEnd(event) {
onDragEnd({
event: event,
props: props
});
if (isValidDropIndex(dragIndexes)) {
onDrop({
oldIndex: dragIndex,
newIndex: dragIndexes.dragOverIndex > dragIndexes.dragIndex ? dragIndexes.dragOverIndex - 1 : dragOverIndex
}, {
event: event,
props: props
});
}
};
var handleDragOver = function handleDragOver(index) {
return function (event) {
event.preventDefault();
if (dragOverIndex !== index) {
onDragOver(index, {
event: event,
props: props
});
}
};
};
var handleDragLeave = function handleDragLeave(event) {
var childCount = findTypes(props, DraggableList.Item).length;
var currentLastItemEl = lastItemEl.current;
if (currentLastItemEl !== null) {
//@ts-ignore
var _currentLastItemEl$ge = currentLastItemEl.getBoundingClientRect(),
bottom = _currentLastItemEl$ge.bottom;
if (_isFinite(dragIndex) && event.clientY > bottom) {
onDragOver(childCount, {
event: event,
props: props
});
}
}
};
var itemChildProps = _map(findTypes(props, DraggableList.Item), 'props');
var dividerIndex = isValidDropIndex(dragIndexes) ? dragIndexes.dragOverIndex : -1;
return /*#__PURE__*/React.createElement("div", _extends({}, omitProps(passThroughs, undefined, _keys(DraggableList.propTypes)), {
className: cx('&', {
'&-is-dragging': _isNumber(dragIndex)
}, className),
style: style,
onDragLeave: handleDragLeave
}), _map(itemChildProps, function (itemChildProp, index) {
return /*#__PURE__*/React.createElement("div", {
key: index
}, /*#__PURE__*/React.createElement("hr", {
className: cx('&-Divider', {
'&-Divider-is-visible': dividerIndex === index
})
}), /*#__PURE__*/React.createElement("div", {
className: cx('&-Item', {
'&-Item-is-dragging': dragIndex === index,
'&-Item-is-drag-over': dragOverIndex === index
}, itemChildProp.className),
draggable: true,
onDragStart: handleDragStart(index),
onDragEnd: handleDragEnd,
onDragOver: handleDragOver(index)
}, /*#__PURE__*/React.createElement("div", _extends({}, itemChildProp, {
className: cx('&-Item-content'),
ref: index === itemChildProps.length - 1 ? lastItemEl : null
})), hasDragHandle && /*#__PURE__*/React.createElement("span", {
className: cx('&-Item-handle')
}, /*#__PURE__*/React.createElement(DotsIcon, {
size: 8
}), /*#__PURE__*/React.createElement(DotsIcon, {
size: 8
}))));
}), /*#__PURE__*/React.createElement("hr", {
key: "divider",
className: cx('&-Divider', {
'&-Divider-is-visible': dividerIndex >= itemChildProps.length
})
}));
};
DraggableList.Item = DraggableListItem;
DraggableList.displayName = 'DraggableList';
DraggableList.peek = {
description: "\n\tThis is a container that renders divs in a list that\n\tcan be drag and drop reordered.\n\t",
categories: ['controls']
};
DraggableList.propTypes = {
className: string,
style: object,
hasDragHandle: bool,
dragIndex: number,
dragOverIndex: number,
onDragStart: func,
onDragEnd: func,
onDragOver: func,
onDrop: func,
Item: PropTypes.any
};
export default buildModernHybridComponent(DraggableList, {
reducers: reducers
});
export { DraggableList as DraggableListDumb };