UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

198 lines (177 loc) 7.68 kB
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 };