From 3a999557f6ddedd6877fb5b89924c0d998b2abe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gaute=20Fl=C3=A6gstad?= Date: Mon, 3 Nov 2025 14:07:13 +0100 Subject: [PATCH] fix(#50): :bug: Selecting a navigation tool with tab should work --- src/Accessibility/makeMenuTabbable.js | 42 ++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Accessibility/makeMenuTabbable.js b/src/Accessibility/makeMenuTabbable.js index 7e34e52..2d3ce43 100644 --- a/src/Accessibility/makeMenuTabbable.js +++ b/src/Accessibility/makeMenuTabbable.js @@ -251,18 +251,58 @@ function makeToolsPanelTabbable() { // Hide camera projection options as they are not supported const cameraProjection = document.getElementById('camera_projection_options'); - cameraProjection.hidden = true + if (cameraProjection) cameraProjection.hidden = true // Make navigation tools tabbable and keyboard clickable const navigationTools = document.querySelectorAll( '#navigation img' ) + const renderArea = document.querySelector('#potree_render_area') + // Keep wrapper focusable as a fallback target (doesn't change tab order elsewhere) + if (renderArea && !renderArea.hasAttribute('tabindex')) { + renderArea.setAttribute('tabindex', '0') + } + navigationTools.forEach((img) => { img.tabIndex = 0 img.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() img.click() + + // Switch controls directly via Potree API, then focus the keyboard input target (canvas) + const pv = window.potreeViewer || window.viewer || null + const target = renderArea + if (pv) { + const src = (img.getAttribute && img.getAttribute('src')) || '' + const title = img.getAttribute && (img.getAttribute('title') || img.getAttribute('data-i18n') || '') + const key = (src + ' ' + title).toLowerCase() + + if (key.includes('earth')) { + pv.setControls(pv.earthControls) + } else if (key.includes('heli') || key.includes('helicopter')) { + pv.setControls(pv.fpControls) + if (pv.fpControls) pv.fpControls.lockElevation = true + } else if (key.includes('fps') || key.includes('flight') || key.includes('flight_control')) { + pv.setControls(pv.fpControls) + if (pv.fpControls) pv.fpControls.lockElevation = false + } else if (key.includes('orbit')) { + pv.setControls(pv.orbitControls) + } + + const dom = pv && pv.renderer && pv.renderer.domElement + if (dom) { + if (!dom.hasAttribute('tabindex')) dom.setAttribute('tabindex', '-1') + dom.focus() + } else if (target) { + // conservative fallback + if (!target.hasAttribute('tabindex')) target.setAttribute('tabindex', '0') + target.focus() + } + } else if (target) { + if (!target.hasAttribute('tabindex')) target.setAttribute('tabindex', '0') + target.focus() + } } }) })