946f16a633
Introduce a full Vite/React UI for exams, auth, materials, images, generation, and export. Adapt backend for Sinbad2IA chat API, bcrypt passwords, CORS on port 5173, and schema migrations.
80 lines
2.0 KiB
React
80 lines
2.0 KiB
React
import { useEffect } from "react";
|
|
import { createPortal } from "react-dom";
|
|
import Button from "./Button";
|
|
import Icon from "./Icon";
|
|
|
|
export default function Modal({ open, onClose, title, children, footer, large }) {
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const onKey = (e) => e.key === "Escape" && onClose?.();
|
|
window.addEventListener("keydown", onKey);
|
|
const prevOverflow = document.body.style.overflow;
|
|
document.body.style.overflow = "hidden";
|
|
return () => {
|
|
window.removeEventListener("keydown", onKey);
|
|
document.body.style.overflow = prevOverflow;
|
|
};
|
|
}, [open, onClose]);
|
|
|
|
if (!open) return null;
|
|
|
|
return createPortal(
|
|
<div className="modal-overlay" onMouseDown={onClose}>
|
|
<div
|
|
className={`modal ${large ? "modal-lg" : ""}`}
|
|
onMouseDown={(e) => e.stopPropagation()}
|
|
>
|
|
{title && (
|
|
<div className="modal-head">
|
|
<h3>{title}</h3>
|
|
<span style={{ flex: 1 }} />
|
|
<button className="toast-close" type="button" aria-label="Cerrar" onClick={onClose}>
|
|
<Icon name="close" size={16} />
|
|
</button>
|
|
</div>
|
|
)}
|
|
<div className="modal-body">{children}</div>
|
|
{footer && <div className="modal-foot">{footer}</div>}
|
|
</div>
|
|
</div>,
|
|
document.body
|
|
);
|
|
}
|
|
|
|
export function ConfirmDialog({
|
|
open,
|
|
title = "¿Confirmar?",
|
|
message,
|
|
confirmLabel = "Confirmar",
|
|
danger,
|
|
loading,
|
|
onConfirm,
|
|
onClose,
|
|
}) {
|
|
return (
|
|
<Modal
|
|
open={open}
|
|
onClose={onClose}
|
|
title={title}
|
|
footer={
|
|
<>
|
|
<Button variant="ghost" onClick={onClose} disabled={loading}>
|
|
Cancelar
|
|
</Button>
|
|
<Button
|
|
variant={danger ? "danger" : "primary"}
|
|
onClick={onConfirm}
|
|
loading={loading}
|
|
>
|
|
{confirmLabel}
|
|
</Button>
|
|
</>
|
|
}
|
|
>
|
|
<p className="text-soft" style={{ margin: 0 }}>
|
|
{message}
|
|
</p>
|
|
</Modal>
|
|
);
|
|
}
|