fix(k8s): YAML 弹框 Portal 到 body 修复居中偏移
PageTransition 的 transform 会使 fixed 弹框相对页面定位而非视口, YamlViewer 尺寸较大时容易跑出可视区。统一用 ModalOverlay Portal。
This commit is contained in:
1 parent
025e74ff4d
commit
729f25e068
4 files changed
+42
-9
No files matched your search
@@ -2,6 +2,7 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { X, Save, Loader2, Plus, Trash2, FileText, Network, Shield, Database, ChevronRight} from 'lucide-react';
|
import { X, Save, Loader2, Plus, Trash2, FileText, Network, Shield, Database, ChevronRight} from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { ModalOverlay } from '@/components/ui/modal-overlay';
|
||||||
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 { Textarea } from '@/components/ui/textarea';
|
||||||
@@ -132,8 +133,11 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
const MetaIcon = meta.icon;
|
const MetaIcon = meta.icon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" onClick={onClose}>
|
<ModalOverlay onClose={onClose}>
|
||||||
<div className="bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-xl w-[90vw] max-w-3xl max-h-[85vh] flex flex-col shadow-2xl" onClick={e => e.stopPropagation()}>
|
<div
|
||||||
|
className="bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-xl w-[90vw] max-w-3xl max-h-[85vh] flex flex-col shadow-2xl"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-[var(--dg-border)]">
|
<div className="flex items-center justify-between p-4 border-b border-[var(--dg-border)]">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
@@ -206,7 +210,7 @@ export function ResourceVisualEditor({ clusterCode, namespace, resource, name, o
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ModalOverlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { X, Upload, Loader2 } from 'lucide-react';
|
import { X, Upload, Loader2 } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { ModalOverlay } from '@/components/ui/modal-overlay';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { kubernetesApi } from '@/api';
|
import { kubernetesApi } from '@/api';
|
||||||
import CodeMirror from '@uiw/react-codemirror';
|
import CodeMirror from '@uiw/react-codemirror';
|
||||||
@@ -36,7 +37,7 @@ export function YamlImportDialog({ clusterCode, onClose, onApplied }: YamlImport
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" onClick={onClose}>
|
<ModalOverlay onClose={onClose}>
|
||||||
<div
|
<div
|
||||||
className="bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-xl w-[92vw] max-w-4xl max-h-[85vh] flex flex-col shadow-2xl"
|
className="bg-[var(--dg-bg-primary)] border border-[var(--dg-border)] rounded-xl w-[92vw] max-w-4xl max-h-[85vh] flex flex-col shadow-2xl"
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
@@ -71,6 +72,6 @@ export function YamlImportDialog({ clusterCode, onClose, onApplied }: YamlImport
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ModalOverlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
import { useState, useEffect, useMemo} from 'react';
|
import { useState, useEffect, useMemo} from 'react';
|
||||||
import { X, Copy, Check, Save, Loader2, Sparkles} from 'lucide-react';
|
import { X, Copy, Check, Save, Loader2, Sparkles} from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { ModalOverlay } from '@/components/ui/modal-overlay';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { kubernetesApi } from '@/api';
|
import { kubernetesApi } from '@/api';
|
||||||
import { cleanYAML } from './k8s-yaml-utils';
|
import { cleanYAML } from './k8s-yaml-utils';
|
||||||
@@ -186,10 +187,13 @@ export function YamlViewer({ clusterCode, namespace, resource, name, onClose, on
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 弹窗模式(原有行为)
|
// 弹窗模式 — Portal 到 body,避免 PageTransition transform 导致偏移 // Logan.Li
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" onClick={onClose}>
|
<ModalOverlay onClose={onClose}>
|
||||||
<div className="bg-slate-900 border border-slate-700 rounded-xl w-[92vw] max-w-5xl h-[88vh] flex flex-col shadow-2xl" onClick={e => e.stopPropagation()}>
|
<div
|
||||||
|
className="bg-slate-900 border border-slate-700 rounded-xl w-[92vw] max-w-5xl max-h-[85vh] flex flex-col shadow-2xl"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-4 border-b border-slate-700">
|
<div className="flex items-center justify-between p-4 border-b border-slate-700">
|
||||||
<div>
|
<div>
|
||||||
@@ -260,6 +264,6 @@ export function YamlViewer({ clusterCode, namespace, resource, name, onClose, on
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ModalOverlay>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// ModalOverlay — Portal 到 body,避免父级 transform 导致 fixed 弹框偏移 // Logan.Li
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
|
interface ModalOverlayProps {
|
||||||
|
onClose: () => void;
|
||||||
|
children: React.ReactNode;
|
||||||
|
/** 遮罩 z-index,默认高于 Sidebar/Header */
|
||||||
|
zIndex?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModalOverlay({ onClose, children, zIndex = 100 }: ModalOverlayProps) {
|
||||||
|
return createPortal(
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 overflow-y-auto bg-black/60"
|
||||||
|
style={{ zIndex }}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<div className="flex min-h-full items-center justify-center p-4">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
document.body,
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user