UNPKG

@mentor-apm/react-sortable-tree

Version:

Drag-and-drop sortable component for nested data and hierarchies

203 lines (200 loc) 7.06 kB
import React, { createRef, useEffect } from 'react'; import throttle from 'lodash.throttle'; import { DndContext } from 'react-dnd'; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; const noop = () => { }; const intBetween = (min, max, val) => Math.floor(Math.min(max, Math.max(min, val))); const getCoords = (evt) => { if (evt.type === "touchmove") { return { x: evt.changedTouches[0].clientX, y: evt.changedTouches[0].clientY }; } return { x: evt.clientX, y: evt.clientY }; }; const DEFAULT_BUFFER = 150; const createHorizontalStrength = (_buffer) => ({ x, w, y, h }, point) => { const buffer = Math.min(w / 2, _buffer); const inRange = point.x >= x && point.x <= x + w; const inBox = inRange && point.y >= y && point.y <= y + h; if (inBox) { if (point.x < x + buffer) { return (point.x - x - buffer) / buffer; } if (point.x > x + w - buffer) { return -(x + w - point.x - buffer) / buffer; } } return 0; }; const createVerticalStrength = (_buffer) => ({ y, h, x, w }, point) => { const buffer = Math.min(h / 2, _buffer); const inRange = point.y >= y && point.y <= y + h; const inBox = inRange && point.x >= x && point.x <= x + w; if (inBox) { if (point.y < y + buffer) { return (point.y - y - buffer) / buffer; } if (point.y > y + h - buffer) { return -(y + h - point.y - buffer) / buffer; } } return 0; }; const defaultProps = { onScrollChange: noop, verticalStrength: createVerticalStrength(DEFAULT_BUFFER), horizontalStrength: createHorizontalStrength(DEFAULT_BUFFER), strengthMultiplier: 30 }; const createScrollingComponent = (WrappedComponent) => { return (props) => { props = __spreadValues(__spreadValues({}, defaultProps), props); let container; const wrappedInstance = createRef(); let frame; let scaleX = 0; let scaleY = 0; let attached = false; let dragging = false; const updateScrolling = throttle((evt) => { const { left: x, top: y, width: w, height: h } = container.getBoundingClientRect(); const box = { x, y, w, h }; const coords = getCoords(evt); scaleX = props.horizontalStrength(box, coords); scaleY = props.verticalStrength(box, coords); if (!frame && (scaleX || scaleY)) { startScrolling(); } }, 100, { trailing: false }); const handleEvent = (evt) => { if (dragging && !attached) { attached = true; window.document.body.addEventListener("dragover", updateScrolling); window.document.body.addEventListener("touchmove", updateScrolling); updateScrolling(evt); } }; useEffect(() => { container = wrappedInstance.current; if (container && typeof container.addEventListener === "function") { container.addEventListener("dragover", handleEvent); } window.document.body.addEventListener("touchmove", handleEvent); const clearMonitorSubscription = props.dragDropManager.getMonitor().subscribeToStateChange(handleMonitorChange); return () => { if (container && typeof container.removeEventListener === "function") { container.removeEventListener("dragover", handleEvent); } window.document.body.removeEventListener("touchmove", handleEvent); clearMonitorSubscription(); stopScrolling(); }; }, []); const handleMonitorChange = () => { const isDragging = props.dragDropManager.getMonitor().isDragging(); if (!dragging && isDragging) { dragging = true; } else if (dragging && !isDragging) { dragging = false; stopScrolling(); } }; const startScrolling = () => { let i = 0; const tick = () => { if (props.strengthMultiplier === 0 || scaleX + scaleY === 0) { stopScrolling(); return; } i += 1; if (i % 2) { const { scrollLeft, scrollTop, scrollWidth, scrollHeight, clientWidth, clientHeight } = container; const newLeft = scaleX ? container.scrollLeft = intBetween(0, scrollWidth - clientWidth, scrollLeft + scaleX * props.strengthMultiplier) : scrollLeft; const newTop = scaleY ? container.scrollTop = intBetween(0, scrollHeight - clientHeight, scrollTop + scaleY * props.strengthMultiplier) : scrollTop; props.onScrollChange(newLeft, newTop); } frame = requestAnimationFrame(tick); }; tick(); }; const stopScrolling = () => { attached = false; window.document.body.removeEventListener("dragover", updateScrolling); window.document.body.removeEventListener("touchmove", updateScrolling); scaleX = 0; scaleY = 0; if (frame) { cancelAnimationFrame(frame); frame = void 0; } }; const _a = props, other = __objRest(_a, [ "strengthMultiplier", "verticalStrength", "horizontalStrength", "onScrollChange" ]); return /* @__PURE__ */ React.createElement(WrappedComponent, __spreadValues({ ref: wrappedInstance }, other)); }; }; const createScrollingComponentWithConsumer = (WrappedComponent) => { const ScrollingComponent = createScrollingComponent(WrappedComponent); return (props) => { return /* @__PURE__ */ React.createElement(DndContext.Consumer, null, ({ dragDropManager }) => !dragDropManager ? void 0 : /* @__PURE__ */ React.createElement(ScrollingComponent, __spreadProps(__spreadValues({}, props), { dragDropManager }))); }; }; export { createHorizontalStrength, createScrollingComponent, createVerticalStrength, createScrollingComponentWithConsumer as default };