fix: toggle aria-hidden on modal open/close for screen reader accessibility
This commit is contained in:
+39
-13
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user