fix: toggle aria-hidden on modal open/close for screen reader accessibility

This commit is contained in:
2026-05-20 23:10:19 -04:00
parent 1a87427295
commit 845523c65b
+39 -13
View File
@@ -1909,7 +1909,7 @@
═══════════════════════════════════════════════════════════════════════ --> ═══════════════════════════════════════════════════════════════════════ -->
<div id="tabNavigate" class="tab-panel"> <div id="tabNavigate" class="tab-panel">
<div class="detail-header"> <div class="detail-header">
<button class="back-btn" onclick="closeNavigate()">← Back</button> <button class="back-btn" onclick="navBackToPrevious()">← Back</button>
<div class="dh-info"> <div class="dh-info">
<div id="navDestName" style="font-size:18px;font-weight:700;">Navigate</div> <div id="navDestName" style="font-size:18px;font-weight:700;">Navigate</div>
<div id="navDestAddr" style="font-size:12px;color:var(--text2);"></div> <div id="navDestAddr" style="font-size:12px;color:var(--text2);"></div>
@@ -2373,7 +2373,7 @@
<!-- ═══════════════════════════════════════════════════════════════════════ <!-- ═══════════════════════════════════════════════════════════════════════
MODAL MODAL
═══════════════════════════════════════════════════════════════════════ --> ═══════════════════════════════════════════════════════════════════════ -->
<div class="modal-overlay" id="modalOverlay"> <div class="modal-overlay" id="modalOverlay" aria-hidden="true">
<div class="modal" id="modalDialog" role="dialog" aria-modal="true" aria-labelledby="modalTitle"> <div class="modal" id="modalDialog" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
<div class="modal-title" id="modalTitle">Confirm</div> <div class="modal-title" id="modalTitle">Confirm</div>
<div class="modal-body" id="modalBody">Are you sure?</div> <div class="modal-body" id="modalBody">Are you sure?</div>
@@ -2920,7 +2920,9 @@
// Save focus and show // Save focus and show
modalFocusRestore = document.activeElement; modalFocusRestore = document.activeElement;
document.getElementById('modalOverlay').classList.add('open'); const overlay = document.getElementById('modalOverlay');
overlay.setAttribute('aria-hidden', 'false');
overlay.classList.add('open');
// Focus the right element // Focus the right element
if (opts.type === 'prompt') { if (opts.type === 'prompt') {
@@ -2937,7 +2939,9 @@
} }
function resolveModal(value) { function resolveModal(value) {
document.getElementById('modalOverlay').classList.remove('open'); const overlay = document.getElementById('modalOverlay');
overlay.classList.remove('open');
overlay.setAttribute('aria-hidden', 'true');
if (modalResolve) { if (modalResolve) {
// For form type, collect input values // For form type, collect input values
if (modalType === 'form') { if (modalType === 'form') {
@@ -4639,7 +4643,10 @@
async function navigateToAsset() { async function navigateToAsset() {
if (!AppState.currentAssetId) return; if (!AppState.currentAssetId) return;
if (!AppState.gpsLat || !AppState.gpsLng) { if (!AppState.gpsLat || !AppState.gpsLng) {
showToast('GPS location not available. Tap ◎ My Location first.', true); showToast('GPS location not available — enable location access to navigate', true);
// Still go to navigate tab to show the GPS warning UI
AppState._navData = null;
switchTab('tabNavigate');
return; return;
} }
try { try {
@@ -4818,24 +4825,40 @@
const clearBtn = document.getElementById('clearNavSearch'); const clearBtn = document.getElementById('clearNavSearch');
if (clearBtn) clearBtn.style.display = q ? '' : 'none'; if (clearBtn) clearBtn.style.display = q ? '' : 'none';
if (!q) { results.innerHTML = ''; return; } if (!q) { results.innerHTML = ''; return; }
const hasGPS = !!(AppState.gpsLat && AppState.gpsLng);
try { try {
const assets = await api('/api/assets?q=' + encodeURIComponent(q) + '&limit=8'); const assets = await api('/api/assets?q=' + encodeURIComponent(q) + '&limit=8');
// Build results header with GPS status
let html = '';
if (!hasGPS) {
html += '<div style="font-size:12px;color:var(--amber);padding:4px 4px 8px;">📍 GPS off — straight-line routing only · <span style="text-decoration:underline;cursor:pointer;" onclick="reEnableGPS()">enable</span></div>';
}
if (!assets.length) { if (!assets.length) {
results.innerHTML = '<div class="empty-state" style="padding:12px;">No assets found</div>'; html += '<div class="empty-state" style="padding:12px;">No assets found</div>';
results.innerHTML = html;
return; return;
} }
results.innerHTML = assets.map(a => {
html += assets.map(a => {
const hasLocation = a.latitude != null && a.longitude != null; const hasLocation = a.latitude != null && a.longitude != null;
return `<div class="asset-item" style="${hasLocation ? '' : 'opacity:0.5;'}" const canNavigate = hasLocation && hasGPS;
onclick="navToSearchedAsset(${a.id})"> const clickAttr = canNavigate ? ` onclick="navToSearchedAsset(${a.id})" style="cursor:pointer;"` : ' style="opacity:0.45;cursor:default;"';
<div class="ai-icon cat-${cssSafe(a.category)}">📍</div> const badge = !hasLocation ? '⚠ No GPS' : (!hasGPS ? '📍' : '🧭');
return `<div class="asset-item"${clickAttr}>
<div class="ai-icon cat-${cssSafe(a.category)}">${catIcon(a.category)}</div>
<div class="ai-info"> <div class="ai-info">
<div class="ai-name">${esc(a.name)}</div> <div class="ai-name">${esc(a.name)}</div>
<div class="ai-meta">${esc(a.machine_id || '')} ${hasLocation ? '📍 ' + esc(a.address || 'GPS tagged') : 'No location'}</div> <div class="ai-meta">${esc(a.machine_id || '')} · ${badge} ${hasLocation ? esc(a.address || 'GPS tagged') : 'No coordinates'}</div>
</div> </div>
<div class="ai-arrow"></div> <div class="ai-arrow">${canNavigate ? '→' : ''}</div>
</div>`; </div>`;
}).join(''); }).join('');
results.innerHTML = html;
} catch (e) { } catch (e) {
results.innerHTML = '<div class="empty-state" style="padding:12px;">Search failed</div>'; results.innerHTML = '<div class="empty-state" style="padding:12px;">Search failed</div>';
} }
@@ -4853,9 +4876,12 @@
async function navToSearchedAsset(assetId) { async function navToSearchedAsset(assetId) {
// Fetch asset details to get navigation data // Fetch asset details to get navigation data
if (!AppState.gpsLat || !AppState.gpsLng) { if (!AppState.gpsLat || !AppState.gpsLng) {
showToast('GPS location not available. Tap ◎ My Location first.', true); showToast('GPS location not available — enable location access to navigate', true);
return; return;
} }
// Show loading feedback
const btn = document.querySelector(`#navSearchResults .asset-item[onclick*="${assetId}"]`);
if (btn) btn.style.opacity = '0.5';
try { try {
const nav = await api('/api/assets/' + assetId + const nav = await api('/api/assets/' + assetId +
'/navigation?lat=' + AppState.gpsLat + '&lng=' + AppState.gpsLng); '/navigation?lat=' + AppState.gpsLat + '&lng=' + AppState.gpsLng);