fluid-dnd
Version:
An agnostic drag and drop library to sort all kind of lists. With current support for vue, react and svelte
141 lines (140 loc) • 6.04 kB
JavaScript
import { HORIZONTAL, VERTICAL } from '.';
import { getDistanceValue, getPropByDirection, getRect, getScrollElementValue, isSameNode } from './utils/GetStyles';
import { getGapPixels } from './utils/ParseStyles';
import { setSizeStyles, setTranistion } from './utils/SetStyles';
import { observeMutation } from './utils/observer';
import getTranslationByDragging from './events/dragAndDrop/getTranslationByDraggingAndEvent';
import { NONE_TRANSLATE, TEMP_CHILD_CLASS } from './utils';
import { addClass, getClassesSelector } from './utils/dom/classList';
const START_DRAG_EVENT = 'startDrag';
const timingFunction = 'cubic-bezier(0.2, 0, 0, 1)';
const DELAY_TIME = 50;
const TRANSITION_PROPERTY = 'width, min-width, height';
const getDistance = (droppable, draggedElement, direction) => {
let distances = getTranslationByDragging(draggedElement, START_DRAG_EVENT, direction, droppable);
const gap = getGapPixels(droppable, direction);
const [, distance] = getDistanceValue(direction, distances);
distances[distance] -= gap;
const [large, largeDistance] = getlarge(direction, draggedElement);
distances[largeDistance] = large;
return distances;
};
const getlarge = (direction, draggedElement) => {
const largeDirection = direction == HORIZONTAL ? VERTICAL : HORIZONTAL;
const distanceValue = getDistanceValue(largeDirection, getRect(draggedElement));
return distanceValue;
};
const setSizes = (element, translate = NONE_TRANSLATE) => {
setSizeStyles(element, translate);
element.style.minWidth = `${translate.width}px`;
};
const updateChildAfterCreated = (child, droppable, distances) => {
return (observer) => {
if (!droppable.contains(child)) {
return;
}
setSizes(child, distances);
observer.disconnect();
};
};
const overflowScroll = (droppable, direction) => {
const { scrollDistance, clientDistance } = getPropByDirection(direction);
return droppable[scrollDistance] - droppable[clientDistance];
};
const scrollPercent = (direction, droppable, droppableScroll) => {
const [scrollElementValue] = getScrollElementValue(direction, droppableScroll);
return scrollElementValue / overflowScroll(droppable, direction);
};
const fixScrollInitialChange = (droppable, config, scroll, ifStartDragging) => {
if (!ifStartDragging) {
return;
}
const { direction } = config;
const scrollCompleted = scrollPercent(direction, droppable, scroll) > 0.99;
const [, scrollElement] = getScrollElementValue(direction, droppable);
if (scrollCompleted) {
droppable[scrollElement] = overflowScroll(droppable, direction);
}
};
const getTempChild = (draggedElement, ifStartDragging, droppableConfig, addingAnimationDuration) => {
const { droppable, config, scroll } = droppableConfig;
const { direction, animationDuration } = config;
fixScrollInitialChange(droppable, config, scroll, ifStartDragging);
if (droppable.querySelector(`.${TEMP_CHILD_CLASS}`) || !draggedElement) {
return;
}
var tempChildTag = draggedElement.tagName == 'LI' ? 'DIV' : draggedElement.tagName;
var child = document.createElement(tempChildTag);
addClass(child, TEMP_CHILD_CLASS);
setSizes(child);
const distances = getDistance(droppable, draggedElement, direction);
setTranistion(child, addingAnimationDuration ?? animationDuration, timingFunction, TRANSITION_PROPERTY);
return [child, distances, droppable];
};
export const addTempChild = (draggedElement, parent, ifStartDragging, droppableConfig, addingAnimationDuration) => {
const result = getTempChild(draggedElement, ifStartDragging, droppableConfig, addingAnimationDuration);
if (!result) {
return;
}
const [child, distances, droppable] = result;
if (isSameNode(parent, droppable)) {
setSizes(child, distances);
}
observeMutation(updateChildAfterCreated(child, droppable, distances), droppable, {
childList: true,
subtree: true
});
droppable.appendChild(child);
};
export const addTempChildOnInsert = (draggedElement, ifStartDragging, droppableConfig) => {
const result = getTempChild(draggedElement, ifStartDragging, droppableConfig);
if (!result) {
return;
}
const [child, distances, droppable] = result;
droppable.appendChild(child);
setSizeAfterAppendChild(child, distances);
};
const setSizeAfterAppendChild = (child, size) => {
return requestAnimationFrame(() => {
setSizes(child, size);
requestAnimationFrame(() => {
setTranistion(child, 0, timingFunction, TRANSITION_PROPERTY);
});
});
};
export const removeTempChildrens = (droppable, parent, droppableGroupClass, animationDuration, draggedElementIsOutside = true) => {
if (!droppableGroupClass) {
return;
}
var children = document.querySelectorAll(`${getClassesSelector(droppableGroupClass)} > .${TEMP_CHILD_CLASS}`);
children.forEach((tempChild) => {
const childParent = tempChild.parentElement;
if (isSameNode(parent, childParent) ||
(!draggedElementIsOutside && isSameNode(droppable, childParent))) {
return;
}
const tempChildElement = tempChild;
setSizes(tempChildElement);
setTimeout(() => {
tempChild.parentNode?.removeChild(tempChild);
}, animationDuration + DELAY_TIME);
});
};
export const removeTempChild = (parent, animationDuration, isAnimated = false) => {
var lastChildren = parent.querySelectorAll(`.${TEMP_CHILD_CLASS}`);
lastChildren.forEach((lastChild) => {
const tempChildElement = lastChild;
if (isAnimated) {
setSizes(tempChildElement);
setTimeout(() => {
if (parent.contains(tempChildElement)) {
parent.removeChild(tempChildElement);
}
}, animationDuration + DELAY_TIME);
}
else {
parent.removeChild(lastChild);
}
});
};