entry_types/scrolled/package/src/frontend/inlineEditing/EditableText/DropTargets.js
import React, {useEffect, useRef, useState} from 'react';
import classNames from 'classnames';
import {Node, Range} from 'slate';
import {useSlate, ReactEditor} from 'slate-react';
import {useDrop} from 'react-dnd';
import {postMoveContentElementMessage} from '../postMessage';
import styles from './DropTargets.module.css';
export function DropTargets({contentElementId}) {
const editor = useSlate();
const [targets, setTargets] = useState();
const containerRef = useRef();
useEffect(() => {
if (!targets) {
setTargets(measureHeights(editor, containerRef.current));
}
}, [targets, editor]);
return (
<div ref={containerRef} className={styles.container}>
{renderDropTargets(targets || [], contentElementId)}
</div>
);
}
function renderDropTargets(targets, contentElementId) {
function handleDrop(item, index) {
if (index === 0) {
postMoveContentElementMessage({
id: item.id,
range: item.range,
to: {
at: 'before',
id: contentElementId
}
});
}
else if (index === targets.length - 1) {
postMoveContentElementMessage({
id: item.id,
range: item.range,
to: {
at: 'after',
id: contentElementId
}
});
}
else {
postMoveContentElementMessage({
id: item.id,
range: item.range,
to: {
at: 'split',
id: contentElementId,
splitPoint: index
}
});
}
}
return targets.map((target, index) =>
<DropTarget {...target}
key={index}
onDrop={item => handleDrop(item, index)} />
);
}
function DropTarget({display, top, height, indicatorTop, onDrop}) {
const [{isOver}, drop] = useDrop({
accept: 'contentElement',
collect: monitor => ({
isOver: monitor.isOver()
}),
drop: item => onDrop(item)
});
return (
<div ref={drop}
className={classNames(styles.dropTarget, {[styles.isOver]: isOver})}
style={{display, top, height}}>
<div className={styles.dropIndicator}
style={{top: indicatorTop}}/>
</div>
);
}
function measureHeights(editor, container) {
const containerRect = container.getBoundingClientRect();
let lastTargetDimensions = {top: 0, height: 0};
let lastRectBottom = 0;
const targetDimensions = editor.children.map((child, index) => {
const node = Node.get(editor, [index]);
const domNode = ReactEditor.toDOMNode(editor, node);
const rect = domNode.getBoundingClientRect();
const top = lastTargetDimensions.top + lastTargetDimensions.height;
const bottom = rect.top + rect.height / 2 - containerRect.top;
const targetDimensions = {
top,
height: bottom - top,
display: editor.selection && Range.includes(editor.selection, [index]) ?
'none' : undefined,
indicatorTop: index > 0 ?
lastRectBottom + (rect.top - lastRectBottom) / 2 - containerRect.top - top :
0
};
lastRectBottom = rect.bottom;
lastTargetDimensions = targetDimensions;
return targetDimensions;
});
return [
...targetDimensions,
{
top: lastTargetDimensions.top + lastTargetDimensions.height,
height: containerRect.height - (lastTargetDimensions.top + lastTargetDimensions.height),
indicatorTop: containerRect.height - (lastTargetDimensions.top + lastTargetDimensions.height)
}
];
}