in src/setter/array-setter/sortable.tsx [18:188]
componentDidMount() {
const box = this.shell!;
let isDragEnd = false;
/**
* target node to be dragged
*/
let source: Element | null;
/**
* node to be placed
*/
let ref: Element | null;
/**
* next sibling of the source node
*/
let origRef: Element | null;
/**
* accurately locate the node from event
*/
const locate = (e: DragEvent) => {
let y = e.clientY;
if (e.view !== window && e.view!.frameElement) {
y += e.view!.frameElement.getBoundingClientRect().top;
}
let node = box.firstElementChild as HTMLDivElement;
while (node) {
if (node !== source && node.dataset.id) {
const rect = node.getBoundingClientRect();
if (rect.height <= 0) continue;
if (y < rect.top + rect.height / 2) {
break;
}
}
node = node.nextElementSibling as HTMLDivElement;
}
return node;
};
/**
* find the source node
*/
const getSource = (e: DragEvent) => {
const target = e.target as Element;
if (!target || !box.contains(target) || target === box) {
return null;
}
let node = box.firstElementChild;
while (node) {
if (node.contains(target)) {
return node;
}
node = node.nextElementSibling;
}
return null;
};
const sort = (beforeId: string | number | null | undefined) => {
if (!source) return;
const sourceId = (source as HTMLDivElement).dataset.id;
const items = this.items!;
const origIndex = items.findIndex((id) => id == sourceId);
let newIndex = beforeId ? items.findIndex((id) => id == beforeId) : items.length;
if (origIndex < 0 || newIndex < 0) return;
if (this.props.onSort) {
if (newIndex > origIndex) {
newIndex -= 1;
}
if (origIndex === newIndex) return;
const item = items.splice(origIndex, 1);
items.splice(newIndex, 0, item[0]);
this.props.onSort(items);
}
};
const dragstart = (e: DragEvent) => {
isDragEnd = false;
source = getSource(e);
if (!source) {
return false;
}
origRef = source.nextElementSibling;
const rect = source.getBoundingClientRect();
let dragSource = source;
if (this.props.dragImageSourceHandler) {
dragSource = this.props.dragImageSourceHandler(source);
}
if (e.dataTransfer) {
e.dataTransfer.setDragImage(dragSource, e.clientX - rect.left, e.clientY - rect.top);
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.dropEffect = 'move';
try {
e.dataTransfer.setData('application/json', {} as any);
} catch (ex) {
// eslint-disable-line
}
}
setTimeout(() => {
source!.classList.add('lc-dragging');
}, 0);
return true;
};
const placeAt = (beforeRef: Element | null) => {
if (beforeRef) {
if (beforeRef !== source) {
box.insertBefore(source!, beforeRef);
}
} else {
box.appendChild(source!);
}
};
const adjust = (e: DragEvent) => {
if (isDragEnd) return;
ref = locate(e);
placeAt(ref);
};
let lastDragEvent: DragEvent | null;
const drag = (e: DragEvent) => {
if (!source) return;
e.preventDefault();
if (lastDragEvent) {
if (lastDragEvent.clientX === e.clientX && lastDragEvent.clientY === e.clientY) {
return;
}
}
lastDragEvent = e;
if (e.dataTransfer) {
e.dataTransfer.effectAllowed = 'move';
}
adjust(e);
};
const dragend = (e: DragEvent) => {
isDragEnd = true;
if (!source) return;
e.preventDefault();
source.classList.remove('lc-dragging');
placeAt(origRef);
sort(ref ? (ref as HTMLDivElement).dataset.id : null);
source = null;
ref = null;
origRef = null;
lastDragEvent = null;
};
box.addEventListener('dragstart', dragstart);
document.addEventListener('dragover', drag);
document.addEventListener('drag', drag);
document.addEventListener('dragend', dragend);
this.willDetach = () => {
box.removeEventListener('dragstart', dragstart);
document.removeEventListener('dragover', drag);
document.removeEventListener('drag', drag);
document.removeEventListener('dragend', dragend);
};
}