@mentor-apm/react-sortable-tree
Version:
Drag-and-drop sortable component for nested data and hierarchies
234 lines (199 loc) • 6.18 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var react = require('react');
var throttle = require('lodash.throttle');
var reactDnd = require('react-dnd');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var throttle__default = /*#__PURE__*/_interopDefaultLegacy(throttle);
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 = { ...defaultProps,
...props
};
let container;
const wrappedInstance = react.createRef();
let frame;
let scaleX = 0;
let scaleY = 0;
let attached = false;
let dragging = false;
const updateScrolling = throttle__default["default"](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);
}
};
react.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 = undefined;
}
};
const {
strengthMultiplier,
verticalStrength,
horizontalStrength,
onScrollChange,
...other
} = props;
return jsxRuntime.jsx(WrappedComponent, {
ref: wrappedInstance,
...other
});
};
};
const createScrollingComponentWithConsumer = WrappedComponent => {
const ScrollingComponent = createScrollingComponent(WrappedComponent);
return props => {
return jsxRuntime.jsx(reactDnd.DndContext.Consumer, {
children: ({
dragDropManager
}) => !dragDropManager ? undefined : jsxRuntime.jsx(ScrollingComponent, { ...props,
dragDropManager: dragDropManager
})
});
};
};
exports.createHorizontalStrength = createHorizontalStrength;
exports.createScrollingComponent = createScrollingComponent;
exports.createVerticalStrength = createVerticalStrength;
exports["default"] = createScrollingComponentWithConsumer;