From 5e34abd1db1d20bdd6486d5557788d7f1848a1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marie=20Wahlstr=C3=B8m?= Date: Tue, 4 Nov 2025 11:06:29 +0100 Subject: [PATCH] feat(#50): :sparkles: Make measurment panel tabbable --- src/Accessibility/makeMenuTabbable.js | 75 +++++++++++++++++---------- src/potreeViewer.js | 6 ++- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/Accessibility/makeMenuTabbable.js b/src/Accessibility/makeMenuTabbable.js index 2d3ce43..1dfa94d 100644 --- a/src/Accessibility/makeMenuTabbable.js +++ b/src/Accessibility/makeMenuTabbable.js @@ -7,6 +7,7 @@ export function makeMenuTabbable() { makePanelsTabbable() makeElevationControlTabbable() makeAcceptedFilteringTabbable() + makeMeasurementsTabbable() makeAppearancePanelTabbable() makeToolsPanelTabbable() } @@ -134,15 +135,15 @@ function makeAcceptedFilteringTabbable() { */ function makeAppearancePanelTabbable() { // Make EDL checkbox tabbable and keyboard clickable - const edlCheckbox = document.getElementById('chkEDLEnabled'); + const edlCheckbox = document.getElementById('chkEDLEnabled') if (edlCheckbox) { edlCheckbox.tabIndex = 0 edlCheckbox.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - edlCheckbox.click(); + e.preventDefault() + edlCheckbox.click() } - }); + }) } // Make background buttons tabbable and keyboard clickable @@ -174,27 +175,27 @@ function makeAppearancePanelTabbable() { }) // Make box checkbox tabbable and keyboard clickable - const boxCheckbox = document.getElementById('show_bounding_box'); + const boxCheckbox = document.getElementById('show_bounding_box') if (boxCheckbox) { boxCheckbox.tabIndex = 0 boxCheckbox.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - boxCheckbox.click(); + e.preventDefault() + boxCheckbox.click() } - }); + }) } // Make lock view checkbox tabbable and keyboard clickable - const lockViewCheckBox = document.getElementById('set_freeze'); + const lockViewCheckBox = document.getElementById('set_freeze') if (lockViewCheckBox) { lockViewCheckBox.tabIndex = 0 lockViewCheckBox.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - lockViewCheckBox.click(); + e.preventDefault() + lockViewCheckBox.click() } - }); + }) } } @@ -203,9 +204,7 @@ function makeAppearancePanelTabbable() { */ function makeToolsPanelTabbable() { // Make clipping tools tabbable and keyboard clickable - const clippingTools = document.querySelectorAll( - '#clipping_tools img' - ) + const clippingTools = document.querySelectorAll('#clipping_tools img') clippingTools.forEach((img, index) => { // Hide unsupported tool if (index === 2) { @@ -222,9 +221,7 @@ function makeToolsPanelTabbable() { }) // Make clip task buttons tabbable and keyboard clickable - const clipTaskButtons = document.querySelectorAll( - '#cliptask_options label' - ) + const clipTaskButtons = document.querySelectorAll('#cliptask_options label') clipTaskButtons.forEach((label) => { label.tabIndex = 0 label.addEventListener('keydown', (e) => { @@ -250,13 +247,11 @@ function makeToolsPanelTabbable() { }) // Hide camera projection options as they are not supported - const cameraProjection = document.getElementById('camera_projection_options'); + const cameraProjection = document.getElementById('camera_projection_options') if (cameraProjection) cameraProjection.hidden = true // Make navigation tools tabbable and keyboard clickable - const navigationTools = document.querySelectorAll( - '#navigation img' - ) + 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')) { @@ -275,7 +270,9 @@ function makeToolsPanelTabbable() { const target = renderArea if (pv) { const src = (img.getAttribute && img.getAttribute('src')) || '' - const title = img.getAttribute && (img.getAttribute('title') || img.getAttribute('data-i18n') || '') + const title = + img.getAttribute && + (img.getAttribute('title') || img.getAttribute('data-i18n') || '') const key = (src + ' ' + title).toLowerCase() if (key.includes('earth')) { @@ -283,7 +280,11 @@ function makeToolsPanelTabbable() { } 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')) { + } 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')) { @@ -292,18 +293,38 @@ function makeToolsPanelTabbable() { const dom = pv && pv.renderer && pv.renderer.domElement if (dom) { - if (!dom.hasAttribute('tabindex')) dom.setAttribute('tabindex', '-1') + if (!dom.hasAttribute('tabindex')) + dom.setAttribute('tabindex', '-1') dom.focus() } else if (target) { // conservative fallback - if (!target.hasAttribute('tabindex')) target.setAttribute('tabindex', '0') + if (!target.hasAttribute('tabindex')) + target.setAttribute('tabindex', '0') target.focus() } } else if (target) { - if (!target.hasAttribute('tabindex')) target.setAttribute('tabindex', '0') + if (!target.hasAttribute('tabindex')) + target.setAttribute('tabindex', '0') target.focus() } } }) }) } + +function makeMeasurementsTabbable() { + // Select every tool inside the measurement tools host + const tools = document.querySelectorAll( + '#measurement_tools_host .tool-with-label' + ) + tools.forEach((tool) => { + tool.tabIndex = 0 + tool.setAttribute('role', 'button') + tool.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + tool.click() + } + }) + }) +} diff --git a/src/potreeViewer.js b/src/potreeViewer.js index 3dc2656..f16ebcd 100644 --- a/src/potreeViewer.js +++ b/src/potreeViewer.js @@ -57,6 +57,8 @@ export async function createPotreeViewer( viewer.loadGUI(() => { viewer.setLanguage('en') + //remove the header with language information + $('#sidebar_header').remove() $('#menu_filters').remove() viewer.toggleSidebar() @@ -127,11 +129,11 @@ export async function createPotreeViewer( // Apply runtime overrides for the 2D Profile tool init2DProfileOverride(viewer) - makeMenuTabbable() - initMeasurementsPanel(viewer) initAnnotationsPanel(viewer) initMiniMap(viewer) + + makeMenuTabbable() }) // Initialize camera position and target point (manually chosen)