diff --git a/src/components/ui/Modal.tsx b/src/components/ui/Modal.tsx index 9db3acf..b8ad21b 100644 --- a/src/components/ui/Modal.tsx +++ b/src/components/ui/Modal.tsx @@ -1,6 +1,19 @@ import React, { useEffect, useRef } from 'react'; +import { createPortal } from 'react-dom'; import { Icon } from './Icon'; +/** + * Dialoge haengen immer direkt am . + * + * Grund: Die Einblendanimationen laufen mit "animation-fill-mode: both". Danach + * behaelt das Element den Wert des letzten Keyframes - bei transform also eine + * Einheitsmatrix statt "none". Das reicht, um einen Bezugsrahmen fuer + * position: fixed zu erzeugen. Steht ein Dialog im Baum unter so einer Karte, + * misst sein Overlay nur noch die Karte und deren overflow: hidden schneidet + * ihn ab (z. B. Budgettopf bearbeiten in der Projektansicht). + */ +const toBody = (node: React.ReactElement) => (typeof document === 'undefined' ? node : createPortal(node, document.body)); + interface ModalProps { title: string; subtitle?: React.ReactNode; @@ -54,7 +67,7 @@ function useDismiss(onClose: () => void) { */ export const Modal: React.FC = ({ title, subtitle, onClose, children, footer, size = 'md' }) => { const dismiss = useDismiss(onClose); - return ( + return toBody(
= ({ onCancel, }) => { const dismiss = useDismiss(onCancel); - return ( + return toBody(