@mentor-apm/react-sortable-tree
Version:
Drag-and-drop sortable component for nested data and hierarchies
203 lines (200 loc) • 7.06 kB
JavaScript
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 };