diff --git a/frontend/src/components/AdminPanel.jsx b/frontend/src/components/AdminPanel.jsx index 7b53847..12ef778 100644 --- a/frontend/src/components/AdminPanel.jsx +++ b/frontend/src/components/AdminPanel.jsx @@ -2,7 +2,19 @@ import React, { useEffect, useState } from "react"; import { api } from "../api/client"; import { styles } from "../styles/styles"; import { stylesTokens } from "../styles/theme"; +import { createPortal } from "react-dom"; + +useEffect(() => { + if (!open) return; + + const prev = document.body.style.overflow; + document.body.style.overflow = "hidden"; + + return () => { + document.body.style.overflow = prev; + }; + }, [open]); export default function AdminPanel() { const [users, setUsers] = useState([]); @@ -112,71 +124,74 @@ export default function AdminPanel() { ))} - {open && ( -
-
e.stopPropagation()}> -
-
- Neuen User anlegen -
- -
- -
- setDisplayName(e.target.value)} - placeholder="Name (z.B. Sascha)" - style={styles.input} - autoFocus - /> - - setEmail(e.target.value)} - placeholder="Email" - style={styles.input} - /> - - setPassword(e.target.value)} - placeholder="Initial Passwort" - type="password" - style={styles.input} - /> - - - - {msg &&
{msg}
} - -
- -
-
- Tipp: Name wird in TopBar & Siegeranzeige genutzt. +
+ setDisplayName(e.target.value)} + placeholder="Name (z.B. Sascha)" + style={styles.input} + autoFocus + /> + + setEmail(e.target.value)} + placeholder="Email" + style={styles.input} + /> + + setPassword(e.target.value)} + placeholder="Initial Passwort" + type="password" + style={styles.input} + /> + + + + {msg &&
{msg}
} + +
+ + +
+ +
+ Tipp: Name wird in TopBar & Siegeranzeige genutzt. +
-
-
- )} +
, + document.body + ) + } ); } diff --git a/frontend/src/components/ModalPortal.jsx b/frontend/src/components/ModalPortal.jsx new file mode 100644 index 0000000..8c02f08 --- /dev/null +++ b/frontend/src/components/ModalPortal.jsx @@ -0,0 +1,40 @@ +import React, { useEffect } from "react"; +import { createPortal } from "react-dom"; +import { styles } from "../styles/styles"; + +export default function ModalPortal({ open, onClose, children }) { + useEffect(() => { + if (!open) return; + + const onKeyDown = (e) => { + if (e.key === "Escape") onClose?.(); + }; + + // Scroll der Seite sperren + const prev = document.body.style.overflow; + document.body.style.overflow = "hidden"; + + window.addEventListener("keydown", onKeyDown); + return () => { + window.removeEventListener("keydown", onKeyDown); + document.body.style.overflow = prev; + }; + }, [open, onClose]); + + if (!open) return null; + + return createPortal( +
{ + // Klick außerhalb schließt + if (e.target === e.currentTarget) onClose?.(); + }} + > +
e.stopPropagation()}> + {children} +
+
, + document.body + ); +} diff --git a/frontend/src/styles/styles.js b/frontend/src/styles/styles.js index d6dff21..3573531 100644 --- a/frontend/src/styles/styles.js +++ b/frontend/src/styles/styles.js @@ -188,32 +188,29 @@ export const styles = { // Modal modalOverlay: { position: "fixed", - inset: 0, - background: "rgba(0,0,0,0.78)", // stärker abdunkeln - backdropFilter: "blur(6px)", // Hintergrund weich (macht viel aus) + top: 0, + left: 0, + right: 0, + bottom: 0, + width: "100vw", + height: "100vh", display: "flex", alignItems: "center", justifyContent: "center", padding: 16, - zIndex: 9999, - animation: "fadeIn 160ms ease-out", - overflowY: "auto", // falls Viewport zu klein + zIndex: 2147483647, // wirklich ganz oben + background: "rgba(0,0,0,0.72)", + overflowY: "auto", }, modalCard: { - width: "100%", - maxWidth: 560, + width: "min(560px, 100%)", borderRadius: 18, border: `1px solid rgba(233,216,166,0.18)`, - background: "linear-gradient(180deg, rgba(20,20,24,0.95), rgba(12,12,14,0.92))", + background: "rgba(12,12,14,0.96)", boxShadow: "0 18px 55px rgba(0,0,0,0.70)", padding: 14, - backdropFilter: "blur(8px)", - animation: "popIn 160ms ease-out", - color: stylesTokens.textMain, - - // neu: damit es nie “kaputt” aussieht - maxHeight: "calc(100dvh - 32px)", + maxHeight: "calc(100vh - 32px)", overflow: "auto", }, modalHeader: {