Kairo-Jinkendo/frontend/src/components/Modal.jsx
Lars e50c04c96f
All checks were successful
Deploy Development / deploy (push) Successful in 53s
Test Suite / pytest-backend (push) Successful in 1m47s
Test Suite / lint-backend (push) Successful in 3s
Test Suite / compose-smoke (push) Has been skipped
Test Suite / k6 /api/health Baseline (push) Successful in 18s
Test Suite / playwright-smoke (push) Successful in 12s
AP1.12b: Modal-Bearbeitung für Profil, Projekte und Backlog im Plan-Modus.
Ersetzt Inline-Formulare durch Dialoge; Scope-Breadcrumb aktualisiert sich nach Profil-Speichern.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 11:26:48 +02:00

69 lines
1.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useId, useRef } from 'react'
/**
* Einfaches Modal (AP1.12b) — Fokus-Falle, Escape schließt.
*
* @param {{ open: boolean, title: string, onClose: () => void, children: import('react').ReactNode, size?: 'md'|'lg' }} props
*/
export function Modal({ open, title, onClose, children, size = 'md' }) {
const titleId = useId()
const dialogRef = useRef(null)
useEffect(() => {
if (!open) return undefined
const previousActive = document.activeElement
dialogRef.current?.focus()
function onKeyDown(event) {
if (event.key === 'Escape') {
event.preventDefault()
onClose()
}
}
document.addEventListener('keydown', onKeyDown)
const prevOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
return () => {
document.removeEventListener('keydown', onKeyDown)
document.body.style.overflow = prevOverflow
if (previousActive instanceof HTMLElement) {
previousActive.focus()
}
}
}, [open, onClose])
if (!open) return null
return (
<div className="modal-root" role="presentation" onMouseDown={onClose}>
<div
ref={dialogRef}
className={`modal-dialog modal-dialog--${size}`}
role="dialog"
aria-modal="true"
aria-labelledby={titleId}
tabIndex={-1}
onMouseDown={(event) => event.stopPropagation()}
>
<header className="modal-dialog__header">
<h2 id={titleId} className="modal-dialog__title">
{title}
</h2>
<button
type="button"
className="modal-dialog__close btn btn-secondary btn-sm"
onClick={onClose}
aria-label="Schließen"
>
×
</button>
</header>
<div className="modal-dialog__body">{children}</div>
</div>
</div>
)
}