modificando el patron del sistema, orientado mas al usuario
This commit is contained in:
232
public/lib/modal.js
Normal file
232
public/lib/modal.js
Normal file
@@ -0,0 +1,232 @@
|
||||
/**
|
||||
* Sistema de modales centralizado para reemplazar alert() nativos
|
||||
* Uso:
|
||||
* import { modal } from './lib/modal.js';
|
||||
* modal.success("Guardado correctamente");
|
||||
* modal.error("Error: " + e.message);
|
||||
* modal.info("Información importante");
|
||||
* modal.warn("Advertencia");
|
||||
* const ok = await modal.confirm("¿Estás seguro?");
|
||||
*/
|
||||
|
||||
const STYLES = `
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
animation: fadeIn 0.15s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { transform: translateY(-20px); opacity: 0; }
|
||||
to { transform: translateY(0); opacity: 1; }
|
||||
}
|
||||
|
||||
.modal-box {
|
||||
background: #1e1e1e;
|
||||
border-radius: 8px;
|
||||
padding: 24px;
|
||||
min-width: 320px;
|
||||
max-width: 480px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||
animation: slideIn 0.2s ease-out;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.modal-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.modal-icon.success { background: #22c55e20; color: #22c55e; }
|
||||
.modal-icon.error { background: #ef444420; color: #ef4444; }
|
||||
.modal-icon.warn { background: #f59e0b20; color: #f59e0b; }
|
||||
.modal-icon.info { background: #3b82f620; color: #3b82f6; }
|
||||
.modal-icon.confirm { background: #8b5cf620; color: #8b5cf6; }
|
||||
|
||||
.modal-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-message {
|
||||
color: #ccc;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 20px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.modal-buttons {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.modal-btn {
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.modal-btn:hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.modal-btn.primary {
|
||||
background: #3b82f6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-btn.secondary {
|
||||
background: #333;
|
||||
color: #ccc;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
|
||||
.modal-btn.danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
`;
|
||||
|
||||
// Inyectar estilos una sola vez
|
||||
let stylesInjected = false;
|
||||
function injectStyles() {
|
||||
if (stylesInjected) return;
|
||||
const style = document.createElement("style");
|
||||
style.textContent = STYLES;
|
||||
document.head.appendChild(style);
|
||||
stylesInjected = true;
|
||||
}
|
||||
|
||||
const ICONS = {
|
||||
success: "✓",
|
||||
error: "✕",
|
||||
warn: "!",
|
||||
info: "i",
|
||||
confirm: "?",
|
||||
};
|
||||
|
||||
const TITLES = {
|
||||
success: "Éxito",
|
||||
error: "Error",
|
||||
warn: "Advertencia",
|
||||
info: "Información",
|
||||
confirm: "Confirmar",
|
||||
};
|
||||
|
||||
function createModal({ type, message, showCancel = false, confirmText = "Aceptar", cancelText = "Cancelar" }) {
|
||||
injectStyles();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const overlay = document.createElement("div");
|
||||
overlay.className = "modal-overlay";
|
||||
|
||||
const box = document.createElement("div");
|
||||
box.className = "modal-box";
|
||||
|
||||
box.innerHTML = `
|
||||
<div class="modal-header">
|
||||
<div class="modal-icon ${type}">${ICONS[type]}</div>
|
||||
<h3 class="modal-title">${TITLES[type]}</h3>
|
||||
</div>
|
||||
<div class="modal-message">${escapeHtml(message)}</div>
|
||||
<div class="modal-buttons">
|
||||
${showCancel ? `<button class="modal-btn secondary" data-action="cancel">${cancelText}</button>` : ""}
|
||||
<button class="modal-btn ${type === "error" ? "danger" : "primary"}" data-action="confirm">${confirmText}</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
overlay.appendChild(box);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
// Focus en el botón principal
|
||||
const confirmBtn = box.querySelector('[data-action="confirm"]');
|
||||
confirmBtn?.focus();
|
||||
|
||||
const close = (result) => {
|
||||
overlay.style.animation = "fadeIn 0.15s ease-out reverse";
|
||||
setTimeout(() => {
|
||||
overlay.remove();
|
||||
resolve(result);
|
||||
}, 140);
|
||||
};
|
||||
|
||||
// Click en botones
|
||||
box.addEventListener("click", (e) => {
|
||||
const action = e.target.dataset?.action;
|
||||
if (action === "confirm") close(true);
|
||||
if (action === "cancel") close(false);
|
||||
});
|
||||
|
||||
// Click fuera cierra (solo para mensajes, no confirms)
|
||||
overlay.addEventListener("click", (e) => {
|
||||
if (e.target === overlay && !showCancel) {
|
||||
close(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Escape cierra
|
||||
const handleKeydown = (e) => {
|
||||
if (e.key === "Escape") {
|
||||
close(showCancel ? false : true);
|
||||
document.removeEventListener("keydown", handleKeydown);
|
||||
}
|
||||
if (e.key === "Enter") {
|
||||
close(true);
|
||||
document.removeEventListener("keydown", handleKeydown);
|
||||
}
|
||||
};
|
||||
document.addEventListener("keydown", handleKeydown);
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement("div");
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
export const modal = {
|
||||
success: (message) => createModal({ type: "success", message }),
|
||||
error: (message) => createModal({ type: "error", message }),
|
||||
warn: (message) => createModal({ type: "warn", message }),
|
||||
info: (message) => createModal({ type: "info", message }),
|
||||
confirm: (message, { confirmText = "Confirmar", cancelText = "Cancelar" } = {}) =>
|
||||
createModal({ type: "confirm", message, showCancel: true, confirmText, cancelText }),
|
||||
};
|
||||
|
||||
// También exportar como default para conveniencia
|
||||
export default modal;
|
||||
Reference in New Issue
Block a user