"use client"; import * as React from "react"; import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; import * as DialogPrimitive from "@radix-ui/react-dialog"; import { cn } from "@/lib/utils"; import { buttonVariants } from "@/components/ui/button"; import { useModalPortal } from "@/lib/modalPortalRef"; import { useTabId } from "@/contexts/TabIdContext"; import { useTabStore } from "@/stores/tabStore"; /** * 탭 시스템 스코프 여부를 하위 컴포넌트에 전달하는 내부 Context. * scoped=true 이면 AlertDialogPrimitive 대신 DialogPrimitive 사용. */ const ScopedAlertCtx = React.createContext(false); const AlertDialog: React.FC> = ({ open, children, onOpenChange, ...props }) => { const autoContainer = useModalPortal(); const scoped = !!autoContainer; const tabId = useTabId(); const activeTabId = useTabStore((s) => s[s.mode].activeTabId); const isTabActive = !tabId || tabId === activeTabId; const isTabActiveRef = React.useRef(isTabActive); isTabActiveRef.current = isTabActive; const effectiveOpen = open != null ? open && isTabActive : undefined; const guardedOnOpenChange = React.useCallback( (newOpen: boolean) => { if (scoped && !newOpen && !isTabActiveRef.current) return; onOpenChange?.(newOpen); }, [scoped, onOpenChange], ); if (scoped) { return ( {children} ); } return ( {children} ); }; AlertDialog.displayName = "AlertDialog"; const AlertDialogTrigger = AlertDialogPrimitive.Trigger; const AlertDialogPortal = AlertDialogPrimitive.Portal; const AlertDialogOverlay = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; interface ScopedAlertDialogContentProps extends React.ComponentPropsWithoutRef { container?: HTMLElement | null; hidden?: boolean; } const AlertDialogContent = React.forwardRef< React.ElementRef, ScopedAlertDialogContentProps >(({ className, container: explicitContainer, hidden: hiddenProp, style, ...props }, ref) => { const autoContainer = useModalPortal(); const container = explicitContainer !== undefined ? explicitContainer : autoContainer; const scoped = React.useContext(ScopedAlertCtx); const adjustedStyle = scoped && style ? { ...style, maxHeight: undefined, maxWidth: undefined } : style; const handleInteractOutside = React.useCallback( (e: any) => { if (scoped && container) { const target = (e.detail?.originalEvent?.target ?? e.target) as HTMLElement | null; if (target && !container.contains(target)) { e.preventDefault(); return; } } e.preventDefault(); }, [scoped, container], ); if (scoped) { return (
e.preventDefault()} onEscapeKeyDown={(e) => e.preventDefault()} className={cn( "bg-background relative z-1 grid w-full max-w-lg max-h-full gap-4 border p-6 shadow-lg sm:rounded-lg", className, )} style={adjustedStyle} {...props} />
); } return (
); }); AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
); AlertDialogHeader.displayName = "AlertDialogHeader"; const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
); AlertDialogFooter.displayName = "AlertDialogFooter"; const AlertDialogTitle = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { const scoped = React.useContext(ScopedAlertCtx); const Comp = scoped ? DialogPrimitive.Title : AlertDialogPrimitive.Title; return ; }); AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; const AlertDialogDescription = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { const scoped = React.useContext(ScopedAlertCtx); const Comp = scoped ? DialogPrimitive.Description : AlertDialogPrimitive.Description; return ; }); AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; const AlertDialogAction = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { const scoped = React.useContext(ScopedAlertCtx); const Comp = scoped ? DialogPrimitive.Close : AlertDialogPrimitive.Action; return ; }); AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; const AlertDialogCancel = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => { const scoped = React.useContext(ScopedAlertCtx); const Comp = scoped ? DialogPrimitive.Close : AlertDialogPrimitive.Cancel; return ( ); }); AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; export { AlertDialog, AlertDialogPortal, AlertDialogOverlay, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, };