ultisuite-client/components/gmail/move-drag-indicator.tsx
2026-05-16 20:30:50 +02:00

83 lines
2.3 KiB
TypeScript

"use client"
import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react"
import { createPortal } from "react-dom"
import { Mail } from "lucide-react"
import { EMAIL_DRAG_RETURN_MS, useEmailDrag } from "@/lib/drag-context"
import {
getDragPointerSnapshot,
subscribeDragPointer,
} from "@/lib/drag-pointer-store"
import { useIsXs } from "@/hooks/use-xs"
const DRAG_POINTER_SERVER_SNAPSHOT = { x: 0, y: 0 } as const
export function MoveDragIndicator() {
const isXs = useIsXs()
const { state } = useEmailDrag()
const [mounted, setMounted] = useState(false)
const elRef = useRef<HTMLDivElement>(null)
const livePointer = useSyncExternalStore(
subscribeDragPointer,
getDragPointerSnapshot,
() => DRAG_POINTER_SERVER_SNAPSHOT
)
useEffect(() => {
setMounted(true)
}, [])
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"
const x = state.phase === "returning" ? state.pointerX : livePointer.x
const y = state.phase === "returning" ? state.pointerY : livePointer.y
return createPortal(
<div
ref={elRef}
aria-hidden
className="pointer-events-none fixed z-100 select-none"
style={{
left: x,
top: y,
transform: "translate(-50%, -50%)",
willChange: "translate, opacity",
}}
>
<div className="flex items-center gap-2 rounded-md bg-[#1a73e8] px-4 py-3 text-sm font-medium text-white shadow-lg">
<Mail className="size-5 shrink-0" strokeWidth={1.75} />
<span className="whitespace-nowrap">{label}</span>
</div>
</div>,
document.body
)
}