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 class="detail-header">
<button class="back-btn" onclick="closeNavigate()">← Back</button>
<button class="back-btn" onclick="navBackToPrevious()">← Back</button>
<div class="dh-info">
<div id="navDestName" style="font-size:18px;font-weight:700;">Navigate</div>
<div id="navDestAddr" style="font-size:12px;color:var(--text2);"></div>
@@ -2373,7 +2373,7 @@
<!-- ═══════════════════════════════════════════════════════════════════════
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-title" id="modalTitle">Confirm</div>
<div class="modal-body" id="modalBody">Are you sure?</div>
@@ -2920,7 +2920,9 @@
// Save focus and show
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
if (opts.type === 'prompt') {
@@ -2937,7 +2939,9 @@
}
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) {
// For form type, collect input values
if (modalType === 'form') {
@@ -4639,7 +4643,10 @@
async function navigateToAsset() {
if (!AppState.currentAssetId) return;
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;
}
try {
@@ -4818,24 +4825,40 @@
const clearBtn = document.getElementById('clearNavSearch');
if (clearBtn) clearBtn.style.display = q ? '' : 'none';
if (!q) { results.innerHTML = ''; return; }
const hasGPS = !!(AppState.gpsLat && AppState.gpsLng);
try {
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) {
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;
}
results.innerHTML = assets.map(a => {
html += assets.map(a => {
const hasLocation = a.latitude != null && a.longitude != null;
return `<div class="asset-item" style="${hasLocation ? '' : 'opacity:0.5;'}"
onclick="navToSearchedAsset(${a.id})">
<div class="ai-icon cat-${cssSafe(a.category)}">📍</div>
const canNavigate = hasLocation && hasGPS;
const clickAttr = canNavigate ? ` onclick="navToSearchedAsset(${a.id})" style="cursor:pointer;"` : ' style="opacity:0.45;cursor:default;"';
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-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 class="ai-arrow"></div>
<div class="ai-arrow">${canNavigate ? '→' : ''}</div>
</div>`;
}).join('');
results.innerHTML = html;
} catch (e) {
results.innerHTML = '<div class="empty-state" style="padding:12px;">Search failed</div>';
}
@@ -4853,9 +4876,12 @@
async function navToSearchedAsset(assetId) {
// Fetch asset details to get navigation data
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;
}
// Show loading feedback
const btn = document.querySelector(`#navSearchResults .asset-item[onclick*="${assetId}"]`);
if (btn) btn.style.opacity = '0.5';
try {
const nav = await api('/api/assets/' + assetId +
'/navigation?lat=' + AppState.gpsLat + '&lng=' + AppState.gpsLng);