"use client" import { useEffect, useLayoutEffect, useRef, useState } from "react" import { createPortal } from "react-dom" import { Mail } from "lucide-react" import { EMAIL_DRAG_RETURN_MS, useEmailDrag } from "@/lib/drag-context" import { useIsXs } from "@/hooks/use-xs" export function MoveDragIndicator() { const isXs = useIsXs() const { state } = useEmailDrag() const [mounted, setMounted] = useState(false) const elRef = useRef(null) useEffect(() => { setMounted(true) }, []) /** * When the user releases the drag outside any valid target the provider * flips `phase` to "returning". We then drive a Web-Animations API tween * from the current cursor position back to the origin (where the drag * started) while fading out, to clearly signal "no move happened". * * `useLayoutEffect` (not `useEffect`) so the animation is queued in the * same paint as the phase flip — otherwise the indicator visibly pauses * for one frame at the release point before animating. * * We animate the `translate` CSS property (NOT `transform`) so the * inline `transform: translate(-50%, -50%)` used for centering stays * untouched and composes additively with the animated translation. */ useLayoutEffect(() => { if (!state) return if (state.phase !== "returning") return const el = elRef.current if (!el) return const dx = state.originX - state.pointerX const dy = state.originY - state.pointerY const animation = el.animate( [ { translate: "0px 0px", opacity: 1 }, { translate: `${dx}px ${dy}px`, opacity: 0 }, ], { duration: EMAIL_DRAG_RETURN_MS, easing: "cubic-bezier(0.22, 0.61, 0.36, 1)", fill: "forwards", } ) return () => { animation.cancel() } }, [state?.phase, state?.originX, state?.originY, state?.pointerX, state?.pointerY]) if (isXs || !mounted || !state) return null const count = state.ids.length const label = count > 1 ? `Déplacer ${count} conversations` : "Déplacer 1 conversation" return createPortal(
{label}
, document.body ) }