UNPKG

@amaui/ui-react

Version:
153 lines (152 loc) 9.58 kB
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; const _excluded = ["onChange", "items", "image", "delay", "precise", "draggedIsElement", "isEqual", "getDraggingElement", "onDraggedElement", "onDragStart", "className", "children"]; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } import React from 'react'; import { debounce, equalDeep, is, isEnvironment } from '@amaui/utils'; import { style as styleMethod, useAmauiTheme } from '@amaui/style-react'; const useStyle = styleMethod(theme => ({ root: {} }), { name: 'amaui-DragAndDropList' }); const DragAndDropList = /*#__PURE__*/React.forwardRef((props_, ref) => { const theme = useAmauiTheme(); const props = React.useMemo(() => _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.amauiDragAndDropList?.props?.default), props_), [props_]); const { onChange: onChange_, items, image, delay = 0, precise = true, draggedIsElement = true, isEqual, getDraggingElement, onDraggedElement, onDragStart: onDragStart_, className, children } = props, other = _objectWithoutProperties(props, _excluded); const { classes } = useStyle(); const refs = { root: React.useRef(undefined), dragging: React.useRef(undefined), rectDragged: React.useRef(undefined), isDragging: React.useRef(), previous: React.useRef(undefined) }; const onChange = React.useMemo(() => { return debounce((indexPrevious, indexNew) => onChange_(indexPrevious, indexNew), delay); }, [onChange_, delay]); React.useEffect(() => { const onMouseUp = event => { // in use case // where onDragEnd is never emited // due to original element having it // has been removed from the dom // prior to onDragEnd event is to be emited // alternative way to provide a callback // to dragging has ended if (refs.isDragging.current) { if (is('function', onDraggedElement)) onDraggedElement(null); refs.isDragging.current = false; } }; const rootDocument = isEnvironment('browser') ? refs.root.current?.ownerDocument || window.document : undefined; rootDocument.body.addEventListener('mouseup', onMouseUp); rootDocument.body.addEventListener('touchend', onMouseUp); return () => { rootDocument.body.removeEventListener('mouseup', onMouseUp); rootDocument.body.removeEventListener('touchend', onMouseUp); }; }, [onDraggedElement]); const img = React.useMemo(() => { const rootDocument = isEnvironment('browser') ? refs.root.current?.ownerDocument || window.document : undefined; const element = rootDocument.createElement('img'); element.src = is('string', image) ? image : `data:image/jpeg;base64,/9j/4AAQSkZJRgABAgEASABIAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARCAAWAC8DAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/igAoAKACgAoAKACgD8gtF+E3xs1/wD4UzrXiHxZ+1dZaj43/au+P/gj4r2Vt8RPjBo1np/7PsfiP42654IE+mxalFB4H8OzzeGvAlr4Z+IWjReH9dbQvE1noei+K10q+8N29l/LWD4Z4vx3+qeLx+Z+JdGvnHiXxvk/E1KGe8VYSlQ4Hjj+L8Zk/Ph44iMMnwM55fk1PL89wkcDjXgswpYPCZksNWwFOj7cq2Hj7eMYYNqng8NUot0qEm8Ty4eNSzt+8klOo50pc0eaDlKHMptyG2+Kml/8Id4f+NFz+1o3wN8JeLf2t/DOnan8MZPjbffFG81Xw18aYdI/Zw1Hx1rHw5gm+K3ijwpdfC9dUuvBvie/uNV8M+JdZh0bVvGt5f2cun3V3Tp8SYb+ysDxbU8TnwdlmaeJ+X0MRw7Li+txHVxOX8XRwvANfOcVkMJcS5jltThxYirlOY16mJy/MMXHCYrN61alKhUql6L55UFgvrE4YKbVX6uqKU6HNilTjVfsYTVaynBJThHmjTSd0ptK039seDSvhH8P/FN18ZH1L9pXwD8JdH8f+OdNu9WnufgFrHwx8RSXnxN1TVNb0O9m0r4ceJ/ir8GbnTdIju9Ney0+f4saPfXtl9o1TUbt5LwtDxVhhuF8jzKpxY8R4gZLwxhc7zihUxM6nBOK4ex0qvEOIxOMwdaeGyHMeJeFKmHwsauHlRoT4lwtatS58RXq8yk8C3WqQVC2FqVpU6bStiY1Y2pJRkr1YUa6crO79jJJ6JE/wmtf2kZv2kLEeLNa+Ltj4lX43/E678cx3+j/ABs1D4UXvwTsz44i8Eabpdxq+saZ+znYeGbvSJ/CR8Lal4I0i7+Ky+K4x/wkti8Uviq/t74Yp8fS4+o/2ni+KKOYLi/iKrnMa+F4vr8M1eEaTziOT4fDTxWKw/AdDL6mFnln9m4jJ8NV4lWZR/4UKLjLMq1MrPC/VXyRoOH1ekqdpYdVliH7P2jajF4pzUufnVSSo8nwP4E/1sr+nTxQoAKACgAoAKACgAD/2Q==`; return element; }, []); const onDragStart = item => async event => { event.dataTransfer.setData('text', item?.value !== undefined ? item.value : item); if (image) event.dataTransfer.setDragImage(img, 0, 0); refs.isDragging.current = true; const dragging = is('function', getDraggingElement) ? getDraggingElement(event) : event.target; if (onDragStart_) onDragStart_(item, event); refs.dragging.current = draggedIsElement ? dragging : dragging?.dataset?.amauiDragAndDropListValue; refs.rectDragged.current = { height: dragging.clientHeight, x: event.clientX - dragging.offsetLeft, y: event.clientY - dragging.offsetTop }; setTimeout(() => { if (is('function', onDraggedElement)) onDraggedElement?.(item); }); }; const onDragEnd = () => event => { // clean up refs.isDragging.current = false; refs.rectDragged.current = null; refs.dragging.current = null; if (is('function', onDraggedElement)) onDraggedElement(null); }; const onDragOver = () => event => { event.preventDefault(); const rootDocument = isEnvironment('browser') ? refs.root.current?.ownerDocument || window.document : undefined; const over = event.currentTarget; const dragging = draggedIsElement ? refs.dragging.current : rootDocument.body.querySelector(`[data-amaui-drag-and-drop-list-value="${refs.dragging.current}"]`); if (!(over && dragging)) return; if (precise) { if (over !== dragging) { const rectOver = { height: over.clientHeight, x: over.offsetLeft, y: over.offsetTop }; const mousePosition = { x: event.clientX - rectOver.x, y: event.clientY - rectOver.y }; const partBottom = Math.abs(refs.rectDragged.current.height - refs.rectDragged.current.y); const partTop = refs.rectDragged.current.y; const half = rectOver.height / 2; const positionTopBottom = partBottom + mousePosition.y >= half ? 'bottom' : 'top'; const positionBottomTop = mousePosition.y - partTop <= half ? 'top' : 'bottom'; const overIndex = items.findIndex(item => isEqual ? isEqual(item, over.dataset.amauiDragAndDropListValue) : item === over.dataset.amauiDragAndDropListValue); const draggedIndex = items.findIndex(item => isEqual ? isEqual(item, dragging.dataset.amauiDragAndDropListValue) : item === dragging.dataset.amauiDragAndDropListValue); // if dragged is above over & bottom swap their indexes // if dragged is below over && top swap their indexes if (draggedIndex < overIndex && positionTopBottom === 'bottom' || draggedIndex > overIndex && positionBottomTop === 'top') { if (!refs.previous.current || !equalDeep(refs.previous.current, [draggedIndex, overIndex])) { onChange(draggedIndex, overIndex); refs.previous.current = [draggedIndex, overIndex]; } } } } else { const overIndex = items.findIndex(item => isEqual ? isEqual(item, over.dataset.amauiDragAndDropListValue) : item === over.dataset.amauiDragAndDropListValue); const draggedIndex = items.findIndex(item => isEqual ? isEqual(item, dragging.dataset.amauiDragAndDropListValue) : item === dragging.dataset.amauiDragAndDropListValue); if (!refs.previous.current || !equalDeep(refs.previous.current, [draggedIndex, overIndex])) { if (is('function', onChange)) onChange(draggedIndex, overIndex); refs.previous.current = [draggedIndex, overIndex]; } } }; if (is('function', children)) return children({ ref: item => { if (ref) { if (is('function', ref)) ref(item);else ref.current = item; } refs.root.current = item; }, onDragStart, onDragOver, onDragEnd }); return /*#__PURE__*/React.createElement(React.Fragment, null, children); }); DragAndDropList.displayName = 'amaui-DragAndDropList'; export default DragAndDropList;