fluid-dnd
Version:
An agnostic drag and drop library to sort all kind of lists. With current support for vue, react and svelte
120 lines (119 loc) • 4.51 kB
JavaScript
import ConfigHandler from './configHandler';
import { draggableIsOutside, isSameNode } from '../utils/GetStyles';
import { IsHTMLElement } from '../utils/typesCheckers';
import { setEventWithInterval } from '../utils/SetStyles';
import { getClassesSelector } from '../utils/dom/classList';
const MapConfig = (coreConfig, mapFrom) => {
const { config, droppable } = coreConfig;
const { onInsertEvent, onDragEnd } = config;
const mapOnInsertEvent = (index, value) => {
return onInsertEvent(index, mapFrom(value, droppable), true);
};
const mapOnDragEnd = (eventData) => {
const { index, value } = eventData;
onDragEnd({ index, value: mapFrom(value, droppable) });
};
return {
...config,
onDragEnd: mapOnDragEnd,
onInsertEvent: mapOnInsertEvent
};
};
export class DroppableConfigurator {
initial;
current;
parent;
draggableElement;
groupClass;
dragEvent;
changeDroppable;
mapFrom;
constructor(draggableElement, droppableGroupClass, parent, setTransformDragEvent, changeDroppable, mapFrom) {
this.parent = parent;
this.draggableElement = draggableElement;
this.groupClass = droppableGroupClass;
this.dragEvent = setTransformDragEvent;
this.mapFrom = mapFrom;
this.initial = ConfigHandler.getConfig(parent);
this.changeDroppable = changeDroppable;
}
getDraggableAncestor(clientX, clientY, draggable) {
return document
.elementsFromPoint(clientX, clientY)
.filter((element) => !isSameNode(draggable, element));
}
getElementBelow(currentElement, event) {
const getElementBelow = (config) => {
const [elementBelow] = config.getDraggableAncestor(event.clientX, event.clientY, currentElement);
return elementBelow;
};
return getElementBelow(this);
}
getCurrent(currentElement, event) {
const elementBelow = this.getElementBelow(currentElement, event);
if (!this.groupClass || !elementBelow) {
return;
}
const currentDroppable = elementBelow.closest(getClassesSelector(this.groupClass));
return currentDroppable;
}
isOutsideOfAllDroppables(currentElement) {
const droppables = this.groupClass
? Array.from(document.querySelectorAll(getClassesSelector(this.groupClass)))
: [this.parent];
return droppables.every((droppable) => draggableIsOutside(currentElement, droppable));
}
isNotInsideAnotherDroppable(currentElement, droppable) {
const isOutside = draggableIsOutside(currentElement, droppable);
return !isOutside || this.isOutsideOfAllDroppables(currentElement);
}
onScrollEvent() {
this.dragEvent();
}
setOnScroll(droppable) {
setEventWithInterval(droppable, 'onscroll', () => {
this.onScrollEvent();
});
}
getConfigFrom(element) {
const coreConfig = ConfigHandler.getConfig(element);
if (!coreConfig) {
return undefined;
}
if (isSameNode(this.parent, element)) {
return coreConfig;
}
return {
...coreConfig,
config: MapConfig(coreConfig, this.mapFrom)
};
}
droppableIfInsideCurrent(droppable, current) {
return droppable && !isSameNode(current, droppable) && current.contains(droppable);
}
getCurrentConfig(event) {
const currentElement = this.draggableElement;
const currentDroppable = this.getCurrent(currentElement, event);
if (this.current &&
this.isNotInsideAnotherDroppable(currentElement, this.current?.droppable) &&
!this.droppableIfInsideCurrent(currentDroppable, this.current?.droppable)) {
return this.current;
}
if (!currentDroppable) {
return this.getConfigFrom(this.parent);
}
if (IsHTMLElement(currentDroppable) && !currentDroppable.onscroll) {
this.setOnScroll(currentDroppable);
}
return this.getConfigFrom(currentDroppable);
}
updateConfig(event) {
const oldDroppableConfig = this.current;
this.current = this.getCurrentConfig(event);
this.changeDroppable(this.current, oldDroppableConfig);
}
isOutside(event) {
const currentElement = this.draggableElement;
return !Boolean(this.getCurrent(currentElement, event));
}
}