fe93aa329c
- Add ProbeAPI client (js/api/probe.js) - Add reusable modal component (js/ui/modal.js) with overlay, animations - Call GET /api/v1/probe after Check Address click - Auto-fill Camera Model with vendor from ARP/OUI lookup - Show modal on unreachable device with Change IP / Continue Anyway buttons - Add modal CSS styles matching existing dark theme
84 lines
2.5 KiB
JavaScript
84 lines
2.5 KiB
JavaScript
/**
|
|
* Simple modal dialog component.
|
|
* Shows a centered card with title, message, and configurable buttons.
|
|
* Returns a Promise that resolves with the clicked button's id.
|
|
*
|
|
* Usage:
|
|
* const result = await showModal({
|
|
* title: 'Device Unreachable',
|
|
* message: 'This IP is not responding.',
|
|
* buttons: [
|
|
* { id: 'change', label: 'Change IP', style: 'primary' },
|
|
* { id: 'continue', label: 'Continue Anyway', style: 'outline' }
|
|
* ]
|
|
* });
|
|
*/
|
|
|
|
let currentResolve = null;
|
|
|
|
export function showModal({ title, message, buttons }) {
|
|
return new Promise((resolve) => {
|
|
currentResolve = resolve;
|
|
|
|
const overlay = document.getElementById('modal-overlay');
|
|
|
|
// Clear previous content safely
|
|
overlay.replaceChildren();
|
|
|
|
// Build modal DOM using safe DOM methods
|
|
const modal = document.createElement('div');
|
|
modal.className = 'modal';
|
|
|
|
const titleEl = document.createElement('div');
|
|
titleEl.className = 'modal-title';
|
|
titleEl.textContent = title;
|
|
modal.appendChild(titleEl);
|
|
|
|
const messageEl = document.createElement('div');
|
|
messageEl.className = 'modal-message';
|
|
messageEl.textContent = message;
|
|
modal.appendChild(messageEl);
|
|
|
|
const actionsEl = document.createElement('div');
|
|
actionsEl.className = 'modal-actions';
|
|
|
|
buttons.forEach(btnConfig => {
|
|
const btn = document.createElement('button');
|
|
btn.className = `btn btn-${btnConfig.style || 'outline'}`;
|
|
btn.textContent = btnConfig.label;
|
|
btn.addEventListener('click', () => {
|
|
hideModal();
|
|
resolve(btnConfig.id);
|
|
});
|
|
actionsEl.appendChild(btn);
|
|
});
|
|
|
|
modal.appendChild(actionsEl);
|
|
overlay.appendChild(modal);
|
|
|
|
// Close on overlay click (outside modal)
|
|
overlay.addEventListener('click', (e) => {
|
|
if (e.target === overlay) {
|
|
hideModal();
|
|
resolve(null);
|
|
}
|
|
});
|
|
|
|
// Show with animation
|
|
overlay.classList.remove('hidden');
|
|
requestAnimationFrame(() => {
|
|
overlay.classList.add('show');
|
|
});
|
|
});
|
|
}
|
|
|
|
export function hideModal() {
|
|
const overlay = document.getElementById('modal-overlay');
|
|
overlay.classList.remove('show');
|
|
setTimeout(() => {
|
|
overlay.classList.add('hidden');
|
|
overlay.replaceChildren();
|
|
}, 200);
|
|
currentResolve = null;
|
|
}
|