components: sticky component (draggable positionable box)
This commit is contained in:
parent
5654b5e15d
commit
50e5ff0663
100
lib/sticky/index.tsx
Normal file
100
lib/sticky/index.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
FC,
|
||||
MouseEventHandler,
|
||||
PropsWithChildren,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Portal } from "../portal/components";
|
||||
|
||||
export const Sticky: FC<
|
||||
PropsWithChildren<{ sidedness: 1 | -1; initialX?: number; initialY?: number }>
|
||||
> = (
|
||||
{ children, sidedness, initialX, initialY },
|
||||
) => {
|
||||
const [position, setPosition] = useState({
|
||||
x: initialX ?? 10,
|
||||
y: initialY ?? 10,
|
||||
});
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (dragging) {
|
||||
setPosition({
|
||||
x: ((sidedness === -1 ? document.body.clientWidth : 0) -
|
||||
(e.pageX - offset.x * sidedness)) * -sidedness,
|
||||
y: e.pageY - offset.y,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
if (dragging) {
|
||||
setDragging(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (dragging) {
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
document.addEventListener("mouseup", handleMouseUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousemove", handleMouseMove);
|
||||
document.removeEventListener("mouseup", handleMouseUp);
|
||||
};
|
||||
}, [dragging, offset, sidedness]);
|
||||
|
||||
const handleMouseDown: MouseEventHandler = (e) => {
|
||||
e.preventDefault();
|
||||
const rect = divRef.current!.getBoundingClientRect();
|
||||
const offsetX = e.pageX - rect.left;
|
||||
const offsetY = e.pageY - rect.top;
|
||||
setOffset({ x: offsetX, y: offsetY });
|
||||
setDragging(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<div
|
||||
className="fixed card p-0 overflow-clip"
|
||||
style={{
|
||||
top: position.y,
|
||||
left: sidedness === 1 ? position.x : "unset",
|
||||
right: sidedness === -1 ? position.x : "unset",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={divRef}
|
||||
className="cursor-move p-1 bg-black/20 flex justify-center"
|
||||
onMouseDown={handleMouseDown}
|
||||
draggable="false"
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
<svg width="70" height="30" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="10" cy="10" r="1" fill="grey" />
|
||||
<circle cx="20" cy="10" r="1" fill="grey" />
|
||||
<circle cx="30" cy="10" r="1" fill="grey" />
|
||||
<circle cx="40" cy="10" r="1" fill="grey" />
|
||||
<circle cx="50" cy="10" r="1" fill="grey" />
|
||||
<circle cx="60" cy="10" r="1" fill="grey" />
|
||||
|
||||
<circle cx="10" cy="20" r="1" fill="grey" />
|
||||
<circle cx="20" cy="20" r="1" fill="grey" />
|
||||
<circle cx="30" cy="20" r="1" fill="grey" />
|
||||
<circle cx="40" cy="20" r="1" fill="grey" />
|
||||
<circle cx="50" cy="20" r="1" fill="grey" />
|
||||
<circle cx="60" cy="20" r="1" fill="grey" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="p-4">{children}</div>
|
||||
</div>
|
||||
</Portal>
|
||||
);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user