Feat/crm mail ui #30

Merged
maaz519 merged 8 commits from feat/crm-mail-ui into goutamnextflow 2026-07-18 12:31:19 +00:00
Showing only changes of commit 28907acf0e - Show all commits
+9 -2
View File
@@ -14,6 +14,7 @@ import {
createContext, useCallback, useContext, useEffect, useId, createContext, useCallback, useContext, useEffect, useId,
useRef, useState, type ReactNode, useRef, useState, type ReactNode,
} from "react"; } from "react";
import { createPortal } from "react-dom";
import { import {
MessageCircle, Ticket, Phone, Mail, BookOpen, Rocket, Shield, ShieldCheck, MessageCircle, Ticket, Phone, Mail, BookOpen, Rocket, Shield, ShieldCheck,
Lock, CreditCard, User, Bell, Eye, EyeOff, Camera, Upload, Plus, Star, Send, Lock, CreditCard, User, Bell, Eye, EyeOff, Camera, Upload, Plus, Star, Send,
@@ -221,6 +222,11 @@ export function Modal({ open, onClose, title, subtitle, icon, children, footer,
children: ReactNode; footer?: ReactNode; size?: "sm" | "md" | "lg"; children: ReactNode; footer?: ReactNode; size?: "sm" | "md" | "lg";
}) { }) {
const titleId = useId(); const titleId = useId();
// Portal the overlay up to `.dash-root` so its position:fixed anchors to the viewport,
// not to a transformed/overflow panel ancestor (which would clip or offset the modal).
const [host, setHost] = useState<Element | null>(null);
const [mounted, setMounted] = useState(false);
useEffect(() => { setHost(document.querySelector(".dash-root")); setMounted(true); }, []);
useEffect(() => { useEffect(() => {
if (!open) return; if (!open) return;
const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); };
@@ -228,8 +234,8 @@ export function Modal({ open, onClose, title, subtitle, icon, children, footer,
return () => document.removeEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey);
}, [open, onClose]); }, [open, onClose]);
if (!open) return null; if (!open || !mounted) return null;
return ( const overlay = (
<div className="ds-modal-overlay" onMouseDown={onClose}> <div className="ds-modal-overlay" onMouseDown={onClose}>
<div className={`ds-modal size-${size}`} role="dialog" aria-modal="true" aria-labelledby={titleId} onMouseDown={(e) => e.stopPropagation()}> <div className={`ds-modal size-${size}`} role="dialog" aria-modal="true" aria-labelledby={titleId} onMouseDown={(e) => e.stopPropagation()}>
<div className="ds-modal-head"> <div className="ds-modal-head">
@@ -247,6 +253,7 @@ export function Modal({ open, onClose, title, subtitle, icon, children, footer,
</div> </div>
</div> </div>
); );
return host ? createPortal(overlay, host) : overlay;
} }
/* ---------------------------------------------------------- */ /* ---------------------------------------------------------- */