fluid-dnd
Version:
An agnostic drag and drop library to sort all kind of lists. With current support for vue, react and svelte
134 lines (133 loc) • 6.35 kB
JavaScript
import { getAxisValue, getBefore, getBeforeMarginValue, getBorderBeforeWidthValue, getDistanceValue, getInnerDistance, getNearestParentPositionWithTranslate, getOffsetValue, getPageValue, getPropByDirection, getRect, getScrollValue, hasTransform, isSameNode } from '../utils/GetStyles';
import { HORIZONTAL, VERTICAL } from '..';
import { useScroll } from './autoScroll';
import { HANDLER_CLASS, DRAGGING_CLASS } from '../utils/classes';
import { containClass } from '../utils/dom/classList';
export const usePositioning = (draggedElement, coordinateTransforms) => {
let currentOffset = { offsetX: 0, offsetY: 0 };
let position = { top: 0, left: 0 };
let translate = { x: 0, y: 0 };
const [updateScrollByPosition] = useScroll(draggedElement);
const updateTranform = (newTranslate) => {
draggedElement.style.transform = `translate( ${newTranslate.x}px, ${newTranslate.y}px)`;
};
const updatePosition = (position) => {
draggedElement.style.top = `${position.top}px`;
draggedElement.style.left = `${position.left}px`;
};
const setTransform = (element, parent, pagePosition, direction) => {
const getTranslateWihtDirection = (translateDirection) => {
const pageValue = getPageValue(translateDirection, pagePosition);
const scrollValue = getScrollValue(translateDirection, window);
const innerDistance = getInnerDistance(translateDirection, window);
const [distanceValue] = getDistanceValue(translateDirection, getRect(element));
const border = getBorderBeforeWidthValue(translateDirection, element);
const margin = getBeforeMarginValue(translateDirection, element);
const elementPosittion = pageValue - getOffsetValue(translateDirection, currentOffset);
const beforefixecParentValue = getNearestParentPositionWithTranslate(element, translateDirection);
if (elementPosittion >= scrollValue - distanceValue / 2 &&
elementPosittion <= scrollValue + innerDistance) {
const parentPosition = getParentPosition(translateDirection, parent);
const newTranslate = elementPosittion -
getBefore(translateDirection, position) -
border -
margin -
scrollValue -
beforefixecParentValue -
parentPosition;
updateScroll(translateDirection);
return newTranslate;
}
const defaultTransalation = getAxisValue(translateDirection, translate);
return defaultTransalation;
};
const updateScroll = (translateDirection) => {
if (element && containClass(element, DRAGGING_CLASS) && translateDirection === direction) {
updateScrollByPosition(direction, parent, position, translate);
}
};
const updateTranlateByDirection = (direction) => {
const { axis } = getPropByDirection(direction);
translate[axis] = getTranslateWihtDirection(direction);
updateTranform(mapCoordinate());
};
updateTranlateByDirection(HORIZONTAL);
updateTranlateByDirection(VERTICAL);
};
const mapCoordinate = () => {
let coordinate = translate;
for (const transform of coordinateTransforms) {
coordinate = transform(coordinate, draggedElement);
}
return coordinate;
};
const updateTransformState = (event, element) => {
const [tempPosition, offset] = getTransformState(event, element, draggedElement);
position = tempPosition;
updatePosition(position);
currentOffset = offset;
};
return [setTransform, updateTransformState];
};
const getOffsetWithDraggable = (direction, element, draggable) => {
return (getBefore(direction, getRect(element)) -
getBefore(direction, getRect(draggable)) -
getBorderBeforeWidthValue(direction, draggable));
};
const getOffset = (event, draggable) => {
let { offsetX, offsetY, target } = event;
let targetHandler = getHandlerElementAncestor(target, draggable);
const targetElement = target;
if (targetElement && targetHandler && !isSameNode(targetElement, targetHandler)) {
offsetX += getOffsetWithDraggable(HORIZONTAL, targetElement, targetHandler);
offsetY += getOffsetWithDraggable(VERTICAL, targetElement, targetHandler);
}
if (targetHandler && draggable != target) {
offsetX += getOffsetWithDraggable(HORIZONTAL, targetHandler, draggable);
offsetY += getOffsetWithDraggable(VERTICAL, targetHandler, draggable);
}
return { offsetX, offsetY };
};
const getHandlerElementAncestor = (target, draggable) => {
const targetHandler = target?.closest(`.${HANDLER_CLASS}`);
if (targetHandler && isSameNode(draggable, targetHandler)) {
return target;
}
return targetHandler;
};
const getPositionByDistance = (direction, event, element, offsetEvent) => {
const beforefixecParentValue = getNearestParentPositionWithTranslate(element, direction);
const parent = element.parentElement;
const parentPosition = getParentPosition(direction, parent);
return (getPageValue(direction, event) -
getOffsetValue(direction, offsetEvent) -
getBeforeMarginValue(direction, element) -
getBorderBeforeWidthValue(direction, element) -
getScrollValue(direction, window) -
beforefixecParentValue -
parentPosition);
};
const getParentPosition = (direction, element) => {
return element && hasAncestorTransform(element) ? getBefore(direction, getRect(element)) : 0;
};
const hasAncestorTransform = (element) => {
let current = element;
while (current) {
const currentElementHasTransform = hasTransform(current);
if (currentElementHasTransform) {
return true;
}
current = current.parentElement;
}
return false;
};
export const getTransformState = (event, element, draggable) => {
const offset = getOffset(event, draggable);
return [
{
top: getPositionByDistance(VERTICAL, event, element, offset),
left: getPositionByDistance(HORIZONTAL, event, element, offset)
},
offset
];
};