Here is the code that I made to implement drag selection.
Item generate code
const items = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
104,
].map((i) => ({ item: i, selected: i === 1 }));
This the actual code responsible for the select
const [data, setData] = useState(items);
const [isSelecting, setIsSelecting] = useState(false);
const [start, setStart] = useState<Coords>({ x: 0, y: 0, screenX: 0, screenY: 0 });
const [end, setEnd] = useState<Coords>({ x: 0, y: 0, screenX: 0, screenY: 0 });
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleMouseDown(e: any) {
if (e.target.closest(".selectable")) return;
setIsSelecting(true);
setStart({ x: e.clientX, y: e.clientY, screenX: e.screenX, screenY: e.screenY });
setEnd({ x: e.clientX, y: e.clientY, screenX: e.screenX, screenY: e.screenY });
setData((data) => [...data.map((item) => ({ ...item, selected: false }))]);
}
ref.current?.addEventListener("mousedown", handleMouseDown);
return () => {
ref.current?.removeEventListener("mousedown", handleMouseDown);
};
}, [ref]);
function handleMouseMove(e: React.MouseEvent<HTMLDivElement>) {
if (!isSelecting) return;
console.log("START");
console.log({ clientX: start.x, clientY: start.y, screenX: start.screenX, screenY: start.screenY });
console.log("END");
console.log({ clientX: e.clientX, clientY: e.clientY, screenX: e.screenX, screenY: e.screenY });
setEnd({ x: e.clientX, y: e.clientY, screenX: e.screenX, screenY: e.screenY });
const selected = [...data];
const elements = document.getElementsByClassName("selectable");
for (let i = 0; i < elements.length; i++) {
const rect = elements[i].getBoundingClientRect();
const elementRect = {
left: rect.left + window.pageXOffset,
top: rect.top + window.pageYOffset,
right: rect.right + window.pageXOffset,
bottom: rect.bottom + window.pageYOffset,
};
if (
((elementRect.left >= Math.min(start.x, end.x) && elementRect.left <= Math.max(start.x, end.x)) ||
(elementRect.right >= Math.min(start.x, end.x) && elementRect.right <= Math.max(start.x, end.x))) &&
((elementRect.top >= Math.min(start.y, end.y) && elementRect.top <= Math.max(start.y, end.y)) ||
(elementRect.bottom >= Math.min(start.y, end.y) && elementRect.bottom <= Math.max(start.y, end.y)))
) {
selected[i].selected = true;
} else {
selected[i].selected = false;
}
}
setData(selected);
}
function handleMouseUp() {
setIsSelecting(false);
reset();
}
const reset = () => {
setStart({ x: 0, y: 0, screenX: 0, screenY: 0 });
setEnd({ x: 0, y: 0, screenX: 0, screenY: 0 });
};
Generate the overlay
const overlayStyle: any = {
position: "absolute",
backgroundColor: colors.slate[800],
opacity: 0.5,
border: "1px dotted",
borderColor: colors.slate[300],
left: `${Math.min(start.x, end.x) - (ref.current?.offsetLeft || 0)}px`,
top: `${Math.min(start.y, end.y) - (ref.current?.offsetTop || 0) + (ref.current?.scrollTop || 0)}px`,
width: `${Math.abs(end.x - start.x)}px`,
height: `${Math.abs(end.y - start.y) + (ref.current?.scrollTop || 0)}px`,
display: isSelecting ? "block" : "none",
pointerEvents: "none",
};
JSX Templete
return (
<>
<div onMouseUp={handleMouseUp} onMouseMove={handleMouseMove} className="relative p-4 overflow-auto bg-slate-600 h-96" ref={ref}>
<ul className="flex flex-wrap gap-2">
{data.map((item) => (
<li
onClick={() => {
console.log("a");
}}
className={cx(
"flex items-center justify-center w-24 text-white hover:border-4 hover:border-slate-500 rounded-lg select-none aspect-square bg-slate-700 selectable cursor-pointer",
{ "border-sky-500 border-4 hover:border-sky-500": item.selected }
)}
key={item.item}
>
Item {item.item}
</li>
))}
</ul>
<div style={overlayStyle}></div>
</div>
<button
onClick={() => {
console.log(ref);
}}
>
1a
</button>
</>
);
All are working correctly,
But the thing is when the elements are significant and show scroll it will not working select as well as the selection overlay.
Thank you for your time!
from I am trying to implement drag select in react from scratch but stuck in the scroll
No comments:
Post a Comment