UNPKG

fluid-dnd

Version:

An agnostic drag and drop library to sort all kind of lists. With current support for vue, react and svelte

202 lines (201 loc) 7.74 kB
import { HORIZONTAL, VERTICAL } from '..'; import { DRAGGABLE_CLASS } from './classes'; import { containClass } from './dom/classList'; export const getWindowScroll = () => { const { scrollX, scrollY } = window; return { scrollX, scrollY }; }; export const parseFloatEmpty = (value) => { if (!value || value.trim().length == 0 || value == 'normal') { return 0; } return parseFloat(value); }; export const parseIntEmpty = (value) => { if (!value) { return -1; } return parseInt(value); }; export const getTransform = (element) => { const style = getComputedStyle(element); const matrix = new DOMMatrixReadOnly(style.transform); return { x: matrix.m41, y: matrix.m42 }; }; const intersection = (firstInterval, secondInterval) => { if (firstInterval.x1 > secondInterval.x1) { return intersection(secondInterval, firstInterval); } if (firstInterval.x2 < secondInterval.x1) { return 0; } if (firstInterval.x2 >= secondInterval.x2) { return firstInterval.x2 - firstInterval.x1; } return firstInterval.x2 - secondInterval.x1; }; export const draggableIsOutside = (draggable, droppable) => { return !hasIntersection(draggable, droppable); }; const hasIntersection = (element1, element2) => { const { intersectionX, intersectionY, rect1, rect2 } = getIntersection(element1, element2); return (intersectionY >= Math.min(rect1.height, rect2.height) / 2 && intersectionX >= Math.min(rect1.width, rect2.width) / 2); }; export const draggableIsCompleteOutside = (draggable, droppable) => { return !hasCompleteIntersection(draggable, droppable); }; const hasCompleteIntersection = (element1, element2) => { const { intersectionX, intersectionY } = getIntersection(element1, element2); return intersectionY >= 0 && intersectionX >= 0; }; const getIntersection = (element1, element2) => { const rect1 = getRect(element1); const rect2 = getRect(element2); const intersectionY = intersectionByDirection(rect1, rect2, VERTICAL); const intersectionX = intersectionByDirection(rect1, rect2, HORIZONTAL); return { rect1, rect2, intersectionX, intersectionY }; }; const intersectionByDirection = (rect1, rect2, direction) => { const { before, distance } = getPropByDirection(direction); return intersection({ x1: rect1[before], x2: rect1[before] + rect1[distance] }, { x1: rect2[before], x2: rect2[before] + rect2[distance] }); }; export const getValueFromProperty = (element, property) => { if (element) { return parseFloatEmpty(getComputedStyle(element)[property]); } return 0; }; export const getScrollElement = (element) => { const { scrollLeft, scrollTop } = element; return { scrollLeft, scrollTop }; }; export const getRect = (element) => { return element.getBoundingClientRect(); }; export const getDistanceValue = (direction, translation) => { const { distance } = getPropByDirection(direction); return [translation[distance], distance]; }; export const getAxisValue = (direction, coordinate) => { const { axis } = getPropByDirection(direction); return coordinate[axis]; }; export const getBorderBeforeWidthValue = (direction, element) => { const { borderBeforeWidth } = getPropByDirection(direction); return getValueFromProperty(element, borderBeforeWidth); }; export const getBeforeMarginValue = (direction, element) => { const { beforeMargin } = getPropByDirection(direction); return getValueFromProperty(element, beforeMargin); }; export const getBeforeMargin = (direction, element) => { const { beforeMargin } = getPropByDirection(direction); return getValueFromProperty(element, beforeMargin); }; export const getAfterMargin = (direction, element) => { const { afterMargin } = getPropByDirection(direction); return getValueFromProperty(element, afterMargin); }; export const getBefore = (direction, elementPosition) => { const { before } = getPropByDirection(direction); return elementPosition[before]; }; export const getScrollElementValue = (direction, element) => { const { scrollElement } = getPropByDirection(direction); return [element[scrollElement], scrollElement]; }; export const getScrollValue = (direction, elementScroll) => { const { scroll } = getPropByDirection(direction); return elementScroll[scroll]; }; export const getInnerDistance = (direction, innerElement) => { const { inner } = getPropByDirection(direction); return innerElement[inner]; }; export const getPageValue = (direction, event) => { const { page } = getPropByDirection(direction); return event[page]; }; export const getOffsetValue = (direction, event) => { const { offset } = getPropByDirection(direction); return event[offset]; }; export const getPropByDirection = (direction) => { const ifHorizontal = direction == HORIZONTAL; return { beforeMargin: ifHorizontal ? 'marginLeft' : 'marginTop', afterMargin: ifHorizontal ? 'marginRight' : 'marginBottom', borderBeforeWidth: ifHorizontal ? 'borderLeftWidth' : 'borderTopWidth', before: ifHorizontal ? 'left' : 'top', gap: ifHorizontal ? 'columnGap' : 'rowGap', distance: ifHorizontal ? 'width' : 'height', axis: ifHorizontal ? 'x' : 'y', offset: ifHorizontal ? 'offsetX' : 'offsetY', scroll: ifHorizontal ? 'scrollX' : 'scrollY', scrollElement: ifHorizontal ? 'scrollLeft' : 'scrollTop', page: ifHorizontal ? 'pageX' : 'pageY', inner: ifHorizontal ? 'innerWidth' : 'innerHeight', scrollDistance: ifHorizontal ? 'scrollWidth' : 'scrollHeight', clientDistance: ifHorizontal ? 'clientWidth' : 'clientHeight', paddingBefore: ifHorizontal ? 'paddingLeft' : 'paddingTop', getRect }; }; export const getSiblings = (current, parent) => { return getSiblingsByParent(current, parent); }; export const getGroupDroppables = (currentDroppable, droppableGroup) => { if (!droppableGroup) { return [currentDroppable]; } return Array.from(document.querySelectorAll(`.droppable-group-${droppableGroup}`)); }; export const getParentDraggableChildren = (parent) => { const siblings = [...parent.children].filter((child) => containClass(child, DRAGGABLE_CLASS)); return siblings; }; export const getSiblingsByParent = (current, parent) => { const siblings = [...parent.children] .filter((child) => containClass(child, DRAGGABLE_CLASS) && !child.isEqualNode(current)) .toReversed(); const positionOnDroppable = [...parent.children].findLastIndex((child) => child.isEqualNode(current)); return [siblings, positionOnDroppable, parent]; }; const getNearestParentWithTranslate = (element) => { let parent = element.parentElement; while (parent) { const computedStyles = window.getComputedStyle(parent); if (computedStyles.translate !== 'none' || computedStyles.willChange === 'transform') { return parent; } parent = parent.parentElement; } return null; // No fixed parent found }; export const getNearestParentPositionWithTranslate = (element, direction) => { const fixedParent = getNearestParentWithTranslate(element); return fixedParent ? getBefore(direction, getRect(fixedParent)) + getBorderBeforeWidthValue(direction, fixedParent) : 0; }; export const isSameNode = (element1, element2) => { return element1?.isSameNode(element2); }; export const hasTransform = (element) => { return element && getComputedStyle(element).transform !== 'none'; };