Refactor: UIManager modularization and Devlog update
This commit is contained in:
196
src/view/ui/FeedbackUI.js
Normal file
196
src/view/ui/FeedbackUI.js
Normal file
@@ -0,0 +1,196 @@
|
||||
export class FeedbackUI {
|
||||
constructor(parentContainer, game) {
|
||||
this.parentContainer = parentContainer;
|
||||
this.game = game; // Needed for resolving hero names/ids in logs?
|
||||
|
||||
this.combatLogContainer = null;
|
||||
this.initCombatLogContainer();
|
||||
}
|
||||
|
||||
initCombatLogContainer() {
|
||||
this.combatLogContainer = document.createElement('div');
|
||||
Object.assign(this.combatLogContainer.style, {
|
||||
position: 'absolute',
|
||||
top: '140px', // Below the top status panel
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
pointerEvents: 'none',
|
||||
width: '100%',
|
||||
maxWidth: '600px',
|
||||
zIndex: '500' // Below modals
|
||||
});
|
||||
this.parentContainer.appendChild(this.combatLogContainer);
|
||||
}
|
||||
|
||||
showModal(title, message, onClose) {
|
||||
const overlay = document.createElement('div');
|
||||
Object.assign(overlay.style, {
|
||||
position: 'absolute', top: '0', left: '0', width: '100%', height: '100%',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.7)', display: 'flex', justifyContent: 'center', alignItems: 'center',
|
||||
pointerEvents: 'auto', zIndex: '1000'
|
||||
});
|
||||
|
||||
const content = document.createElement('div');
|
||||
Object.assign(content.style, {
|
||||
backgroundColor: '#222', border: '2px solid #888', borderRadius: '8px', padding: '20px',
|
||||
width: '300px', textAlign: 'center', color: '#fff', fontFamily: 'sans-serif'
|
||||
});
|
||||
|
||||
const titleEl = document.createElement('h2');
|
||||
titleEl.textContent = title;
|
||||
Object.assign(titleEl.style, { marginTop: '0', color: '#f44' });
|
||||
content.appendChild(titleEl);
|
||||
|
||||
const msgEl = document.createElement('p');
|
||||
msgEl.innerHTML = message;
|
||||
Object.assign(msgEl.style, { fontSize: '16px', lineHeight: '1.5' });
|
||||
content.appendChild(msgEl);
|
||||
|
||||
const btn = document.createElement('button');
|
||||
btn.textContent = 'Entendido';
|
||||
Object.assign(btn.style, {
|
||||
marginTop: '20px', padding: '10px 20px', fontSize: '16px', cursor: 'pointer',
|
||||
backgroundColor: '#444', color: '#fff', border: '1px solid #888'
|
||||
});
|
||||
btn.onclick = () => {
|
||||
if (overlay.parentNode /** Checks if attached */) this.parentContainer.removeChild(overlay);
|
||||
if (onClose) onClose();
|
||||
};
|
||||
content.appendChild(btn);
|
||||
|
||||
overlay.appendChild(content);
|
||||
this.parentContainer.appendChild(overlay);
|
||||
}
|
||||
|
||||
showConfirm(title, message, onConfirm) {
|
||||
const overlay = document.createElement('div');
|
||||
Object.assign(overlay.style, {
|
||||
position: 'absolute', top: '0', left: '0', width: '100%', height: '100%',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.7)', display: 'flex', justifyContent: 'center', alignItems: 'center',
|
||||
pointerEvents: 'auto', zIndex: '1000'
|
||||
});
|
||||
|
||||
const content = document.createElement('div');
|
||||
Object.assign(content.style, {
|
||||
backgroundColor: '#222', border: '2px solid #888', borderRadius: '8px', padding: '20px',
|
||||
width: '300px', textAlign: 'center', color: '#fff', fontFamily: 'sans-serif'
|
||||
});
|
||||
|
||||
const titleEl = document.createElement('h2');
|
||||
titleEl.textContent = title;
|
||||
Object.assign(titleEl.style, { marginTop: '0', color: '#f44' });
|
||||
content.appendChild(titleEl);
|
||||
|
||||
const msgEl = document.createElement('p');
|
||||
msgEl.innerHTML = message;
|
||||
Object.assign(msgEl.style, { fontSize: '16px', lineHeight: '1.5' });
|
||||
content.appendChild(msgEl);
|
||||
|
||||
const buttons = document.createElement('div');
|
||||
Object.assign(buttons.style, { display: 'flex', justifyContent: 'space-around', marginTop: '20px' });
|
||||
|
||||
const cancelBtn = document.createElement('button');
|
||||
cancelBtn.textContent = 'Cancelar';
|
||||
Object.assign(cancelBtn.style, {
|
||||
padding: '10px 20px', fontSize: '16px', cursor: 'pointer',
|
||||
backgroundColor: '#555', color: '#fff', border: '1px solid #888'
|
||||
});
|
||||
cancelBtn.onclick = () => { this.parentContainer.removeChild(overlay); };
|
||||
buttons.appendChild(cancelBtn);
|
||||
|
||||
const confirmBtn = document.createElement('button');
|
||||
confirmBtn.textContent = 'Aceptar';
|
||||
Object.assign(confirmBtn.style, {
|
||||
padding: '10px 20px', fontSize: '16px', cursor: 'pointer',
|
||||
backgroundColor: '#2a5', color: '#fff', border: '1px solid #888'
|
||||
});
|
||||
confirmBtn.onclick = () => {
|
||||
if (onConfirm) onConfirm();
|
||||
this.parentContainer.removeChild(overlay);
|
||||
};
|
||||
buttons.appendChild(confirmBtn);
|
||||
|
||||
content.appendChild(buttons);
|
||||
overlay.appendChild(content);
|
||||
this.parentContainer.appendChild(overlay);
|
||||
}
|
||||
|
||||
showTemporaryMessage(title, message, duration = 2000) {
|
||||
const modal = document.createElement('div');
|
||||
Object.assign(modal.style, {
|
||||
position: 'absolute', top: '25%', left: '50%', transform: 'translate(-50%, -50%)',
|
||||
backgroundColor: 'rgba(139, 0, 0, 0.9)', color: '#fff', padding: '15px 30px',
|
||||
borderRadius: '8px', border: '2px solid #ff4444', fontFamily: '"Cinzel", serif',
|
||||
fontSize: '20px', textShadow: '2px 2px 4px black', zIndex: '2000', pointerEvents: 'none',
|
||||
opacity: '0', transition: 'opacity 0.5s ease-in-out'
|
||||
});
|
||||
|
||||
modal.innerHTML = `
|
||||
<h3 style="margin:0; text-align:center; color: #FFD700; text-transform: uppercase;">⚠️ ${title}</h3>
|
||||
<div style="margin-top:5px; font-size: 16px;">${message}</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
requestAnimationFrame(() => { modal.style.opacity = '1'; });
|
||||
|
||||
setTimeout(() => {
|
||||
modal.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
if (modal.parentNode) document.body.removeChild(modal);
|
||||
}, 500);
|
||||
}, duration);
|
||||
}
|
||||
|
||||
showCombatLog(log) {
|
||||
const isHit = log.hitSuccess;
|
||||
const color = isHit ? '#ff4444' : '#aaaaaa';
|
||||
|
||||
let detailHtml = '';
|
||||
if (isHit) {
|
||||
if (log.woundsCaused > 0) {
|
||||
detailHtml = `<div style="font-size: 24px; color: #ff0000; font-weight:bold;">-${log.woundsCaused} HP</div>`;
|
||||
} else {
|
||||
detailHtml = `<div style="font-size: 20px; color: #aaa;">Sin Heridas (Armadura)</div>`;
|
||||
}
|
||||
} else {
|
||||
detailHtml = `<div style="font-size: 18px; color: #888;">Esquivado / Fallado</div>`;
|
||||
}
|
||||
|
||||
// We create a new log element or update a singleton?
|
||||
// The original logic updated a SINGLE notification area.
|
||||
// Let's create a transient toast style log here, appending to container.
|
||||
|
||||
const logItem = document.createElement('div');
|
||||
Object.assign(logItem.style, {
|
||||
backgroundColor: 'rgba(0,0,0,0.9)', padding: '15px', border: `2px solid ${color}`,
|
||||
borderRadius: '5px', textAlign: 'center', minWidth: '250px', marginBottom: '10px',
|
||||
fontFamily: '"Cinzel", serif', opacity: '0', transition: 'opacity 0.3s'
|
||||
});
|
||||
|
||||
logItem.innerHTML = `
|
||||
<div style="font-size: 18px; color: ${color}; margin-bottom: 5px; text-transform:uppercase;">${log.attackerId.split('_')[0]} ATACA</div>
|
||||
${detailHtml}
|
||||
<div style="font-size: 14px; color: #ccc; margin-top:5px;">${log.message}</div>
|
||||
`;
|
||||
|
||||
// Clear previous logs to act like the single notification area of before, OR stack them?
|
||||
// Original behavior was overwrite `innerHTML`. I should stick to that to avoid spam.
|
||||
// So I will clear `combatLogContainer` before adding.
|
||||
this.combatLogContainer.innerHTML = '';
|
||||
this.combatLogContainer.appendChild(logItem);
|
||||
|
||||
// Fade in
|
||||
requestAnimationFrame(() => { logItem.style.opacity = '1'; });
|
||||
|
||||
// Fade out
|
||||
setTimeout(() => {
|
||||
logItem.style.opacity = '0';
|
||||
// We don't remove immediately to avoid layout jumps if another comes in,
|
||||
// but we cleared logic above.
|
||||
}, 3500);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user