reactour
reactour copied to clipboard
Feature Request : Making popovers draggable
I think it would be a good enhancement if the popover could be made draggable. In one of the steps of the onboarding process that I have done, the entire screen is highlighted, and allowing users to move the popover would provide greater flexibility and convenience.
Let me know what you think!
You can implement this by yourself actually, just create a customContentComponent and wrap that with a draggable div.
Here is an example code
import { cn } from "@/lib/utils";
import { Grip } from "lucide-react";
import React, { useEffect, useState } from "react";
interface DraggablePopoverProps {
children: React.ReactNode;
currentStep: number;
draggable: boolean | undefined;
}
export const DraggablePopover = ({
children,
currentStep,
draggable,
}: DraggablePopoverProps) => {
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
const [isDragging, setIsDragging] = useState(false);
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
// Reset position when step changes
useEffect(() => {
setDragOffset({ x: 0, y: 0 });
}, [currentStep]);
const handleMouseDown = (e: React.MouseEvent) => {
if (e.button !== 0) return; // Only handle left click
e.stopPropagation();
setIsDragging(true);
setDragStart({
x: e.clientX - dragOffset.x,
y: e.clientY - dragOffset.y,
});
};
useEffect(() => {
if (!draggable) return;
const handleMouseMove = (e: MouseEvent) => {
if (!isDragging) return;
e.stopPropagation();
setDragOffset({
x: e.clientX - dragStart.x,
y: e.clientY - dragStart.y,
});
};
const handleMouseUp = (e: MouseEvent) => {
if (!isDragging) return;
e.stopPropagation();
setIsDragging(false);
};
if (isDragging) {
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
}
return () => {
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
}, [isDragging, dragStart, draggable]);
return (
<div
data-tour="wrapper-popover"
style={{
transform: `translate(${dragOffset.x}px, ${dragOffset.y}px)`,
position: "relative",
zIndex: 9999,
pointerEvents: "auto",
}}
>
<div className="bg-popover shadow-primary rounded-lg p-4 dark:border dark:shadow-none">
{draggable && (
<div
className="bg-muted/10 absolute left-0 right-0 top-0 flex h-8 cursor-grab items-center justify-center rounded-t-lg active:cursor-grabbing"
onMouseDown={handleMouseDown}
>
<Grip className="text-muted-foreground/40 h-4 w-4" />
</div>
)}
<div className={cn(draggable ? "mt-6" : "mt-0")}>{children}</div>
</div>
</div>
);
};