모달 열렸을 떄 위젯이 끌리는 현상 해결

This commit is contained in:
dohyeons 2025-10-17 18:00:27 +09:00
parent 8b28107147
commit 809ded3746
3 changed files with 34 additions and 38 deletions

View File

@ -162,6 +162,11 @@ export function CanvasElement({
// 요소 선택 처리 // 요소 선택 처리
const handleMouseDown = useCallback( const handleMouseDown = useCallback(
(e: React.MouseEvent) => { (e: React.MouseEvent) => {
// 모달이나 다이얼로그가 열려있으면 드래그 무시
if (document.querySelector('[role="dialog"]')) {
return;
}
// 닫기 버튼이나 리사이즈 핸들 클릭 시 무시 // 닫기 버튼이나 리사이즈 핸들 클릭 시 무시
if ((e.target as HTMLElement).closest(".element-close, .resize-handle")) { if ((e.target as HTMLElement).closest(".element-close, .resize-handle")) {
return; return;
@ -192,6 +197,11 @@ export function CanvasElement({
// 리사이즈 핸들 마우스다운 // 리사이즈 핸들 마우스다운
const handleResizeMouseDown = useCallback( const handleResizeMouseDown = useCallback(
(e: React.MouseEvent, handle: string) => { (e: React.MouseEvent, handle: string) => {
// 모달이나 다이얼로그가 열려있으면 리사이즈 무시
if (document.querySelector('[role="dialog"]')) {
return;
}
e.stopPropagation(); e.stopPropagation();
setIsResizing(true); setIsResizing(true);
setResizeStart({ setResizeStart({
@ -522,16 +532,15 @@ export function CanvasElement({
<span className="text-sm font-bold text-gray-800">{element.customTitle || element.title}</span> <span className="text-sm font-bold text-gray-800">{element.customTitle || element.title}</span>
<div className="flex gap-1"> <div className="flex gap-1">
{/* 설정 버튼 (기사관리 위젯만 자체 설정 UI 사용) */} {/* 설정 버튼 (기사관리 위젯만 자체 설정 UI 사용) */}
{onConfigure && {onConfigure && !(element.type === "widget" && element.subtype === "driver-management") && (
!(element.type === "widget" && element.subtype === "driver-management") && ( <button
<button className="hover:bg-accent0 flex h-6 w-6 items-center justify-center rounded text-gray-400 transition-colors duration-200 hover:text-white"
className="hover:bg-accent0 flex h-6 w-6 items-center justify-center rounded text-gray-400 transition-colors duration-200 hover:text-white" onClick={() => onConfigure(element)}
onClick={() => onConfigure(element)} title="설정"
title="설정" >
>
</button>
</button> )}
)}
{/* 삭제 버튼 */} {/* 삭제 버튼 */}
<button <button
className="element-close hover:bg-destructive/100 flex h-6 w-6 items-center justify-center rounded text-gray-400 transition-colors duration-200 hover:text-white" className="element-close hover:bg-destructive/100 flex h-6 w-6 items-center justify-center rounded text-gray-400 transition-colors duration-200 hover:text-white"

View File

@ -67,9 +67,9 @@ export default function YardManagement3DWidget({
}; };
// 새 레이아웃 생성 // 새 레이아웃 생성
const handleCreateLayout = async (name: string, description: string) => { const handleCreateLayout = async (name: string) => {
try { try {
const response = await yardLayoutApi.createLayout({ name, description }); const response = await yardLayoutApi.createLayout({ name });
if (response.success) { if (response.success) {
await loadLayouts(); await loadLayouts();
setIsCreateModalOpen(false); setIsCreateModalOpen(false);
@ -105,7 +105,7 @@ export default function YardManagement3DWidget({
// 편집 모드: 레이아웃 선택 UI // 편집 모드: 레이아웃 선택 UI
if (isEditMode) { if (isEditMode) {
return ( return (
<div className="flex h-full w-full flex-col bg-white"> <div className="widget-interactive-area flex h-full w-full flex-col bg-white">
<div className="flex items-center justify-between border-b p-4"> <div className="flex items-center justify-between border-b p-4">
<div> <div>
<h3 className="text-sm font-semibold text-gray-700"> </h3> <h3 className="text-sm font-semibold text-gray-700"> </h3>

View File

@ -12,18 +12,17 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea"; import { Alert, AlertDescription } from "@/components/ui/alert";
import { Loader2 } from "lucide-react"; import { Loader2, AlertCircle } from "lucide-react";
interface YardLayoutCreateModalProps { interface YardLayoutCreateModalProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
onCreate: (name: string, description: string) => Promise<void>; onCreate: (name: string) => Promise<void>;
} }
export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: YardLayoutCreateModalProps) { export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: YardLayoutCreateModalProps) {
const [name, setName] = useState(""); const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [isCreating, setIsCreating] = useState(false); const [isCreating, setIsCreating] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
@ -38,9 +37,8 @@ export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: Yar
setError(""); setError("");
try { try {
await onCreate(name.trim(), description.trim()); await onCreate(name.trim());
setName(""); setName("");
setDescription("");
} catch (error: any) { } catch (error: any) {
console.error("야드 생성 실패:", error); console.error("야드 생성 실패:", error);
setError(error.message || "야드 생성에 실패했습니다"); setError(error.message || "야드 생성에 실패했습니다");
@ -53,7 +51,6 @@ export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: Yar
const handleClose = () => { const handleClose = () => {
if (isCreating) return; if (isCreating) return;
setName(""); setName("");
setDescription("");
setError(""); setError("");
onClose(); onClose();
}; };
@ -68,14 +65,13 @@ export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: Yar
return ( return (
<Dialog open={isOpen} onOpenChange={handleClose}> <Dialog open={isOpen} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[500px]"> <DialogContent className="sm:max-w-[500px]" onPointerDown={(e) => e.stopPropagation()}>
<DialogHeader> <DialogHeader>
<DialogTitle> </DialogTitle> <DialogTitle> </DialogTitle>
<DialogDescription> </DialogDescription> <DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4">
{/* 야드 이름 */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="yard-name"> <Label htmlFor="yard-name">
<span className="text-red-500">*</span> <span className="text-red-500">*</span>
@ -94,21 +90,12 @@ export default function YardLayoutCreateModal({ isOpen, onClose, onCreate }: Yar
/> />
</div> </div>
{/* 설명 */} {error && (
<div className="space-y-2"> <Alert variant="destructive">
<Label htmlFor="yard-description"></Label> <AlertCircle className="h-4 w-4" />
<Textarea <AlertDescription>{error}</AlertDescription>
id="yard-description" </Alert>
value={description} )}
onChange={(e) => setDescription(e.target.value)}
placeholder="야드에 대한 설명을 입력하세요 (선택사항)"
rows={3}
disabled={isCreating}
/>
</div>
{/* 에러 메시지 */}
{error && <div className="rounded-md bg-red-50 p-3 text-sm text-red-600">{error}</div>}
</div> </div>
<DialogFooter> <DialogFooter>