ultisuite-client/components/agenda/agenda-floating-card.tsx
R3D347HR4Y 3bbf3691b0
Some checks failed
E2E / Playwright e2e (push) Has been cancelled
bordel c'est beau
2026-06-11 10:10:39 +02:00

97 lines
2.5 KiB
TypeScript

"use client"
import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from "react"
import { createPortal } from "react-dom"
import { cn } from "@/lib/utils"
export interface AnchorRect {
left: number
top: number
width: number
height: number
}
/**
* Carte flottante ancrée à un rectangle (popover détails / création rapide),
* positionnée en `fixed` et recadrée dans la fenêtre.
*/
export function AgendaFloatingCard({
anchor,
onClose,
children,
className,
width = 416,
}: {
anchor: AnchorRect
onClose: () => void
children: ReactNode
className?: string
width?: number
}) {
const cardRef = useRef<HTMLDivElement>(null)
const [pos, setPos] = useState<{ left: number; top: number } | null>(null)
useLayoutEffect(() => {
const card = cardRef.current
if (!card) return
const margin = 8
const vw = window.innerWidth
const vh = window.innerHeight
const rect = card.getBoundingClientRect()
const w = Math.min(width, vw - margin * 2)
const h = rect.height
// Préférence : à droite de l'ancre, sinon à gauche, sinon en dessous.
let left = anchor.left + anchor.width + margin
if (left + w > vw - margin) left = anchor.left - w - margin
if (left < margin) left = Math.min(Math.max(margin, anchor.left), vw - w - margin)
let top = anchor.top
if (top + h > vh - margin) top = vh - h - margin
if (top < margin) top = margin
setPos({ left, top })
}, [anchor, width, children])
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") {
e.stopPropagation()
onClose()
}
}
window.addEventListener("keydown", onKey, true)
return () => window.removeEventListener("keydown", onKey, true)
}, [onClose])
if (typeof document === "undefined") return null
return createPortal(
<div className="fixed inset-0 z-[60]">
<button
type="button"
aria-label="Fermer"
className="absolute inset-0 cursor-default"
onClick={onClose}
/>
<div
ref={cardRef}
role="dialog"
className={cn(
"absolute flex max-h-[85vh] flex-col overflow-hidden rounded-xl border border-border/60 bg-popover text-popover-foreground shadow-2xl",
className,
)}
style={{
width: Math.min(width, window.innerWidth - 16),
left: pos?.left ?? -9999,
top: pos?.top ?? -9999,
visibility: pos ? "visible" : "hidden",
}}
>
{children}
</div>
</div>,
document.body,
)
}