From 1487d0b2eb27289cfd71b2847061184439e32820 Mon Sep 17 00:00:00 2001 From: braimbault Date: Fri, 13 Dec 2024 16:21:43 +0100 Subject: [PATCH] feat: additional keyboard controls --- src/components/datatable/BottomPanel.js | 3 ++ src/components/datatable/DataTable.js | 2 +- .../edit/thematic/ThematicDialog.js | 1 - .../interpretations/InterpretationsToggle.js | 9 ++++++ src/components/map/Popup.js | 3 ++ src/components/orgunits/OrgUnitProfile.js | 3 ++ src/hooks/useKeyDown.js | 32 ++++++++++++++++--- 7 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/components/datatable/BottomPanel.js b/src/components/datatable/BottomPanel.js index 82a15c6e5..97a5018a5 100644 --- a/src/components/datatable/BottomPanel.js +++ b/src/components/datatable/BottomPanel.js @@ -8,6 +8,7 @@ import { LAYERS_PANEL_WIDTH, RIGHT_PANEL_WIDTH, } from '../../constants/layout.js' +import useKeyDown from '../../hooks/useKeyDown.js' import { useWindowDimensions } from '../WindowDimensionsProvider.js' import DataTable from './DataTable.js' import ErrorBoundary from './ErrorBoundary.js' @@ -37,6 +38,8 @@ const BottomPanel = () => { const tableWidth = width - layersWidth - rightPanelWidth const dataTableControlsHeight = 20 + useKeyDown('Escape', () => dispatch(closeDataTable()), true) + return (
{ }) useEffect(() => { - /* The combination of automtic table layout and virtual scrolling + /* The combination of automatic table layout and virtual scrolling * causes a content shift when scrolling and filtering because the * cells in the DOM have a different content length which causes the * columns to have a different width. To avoid that we measure the diff --git a/src/components/edit/thematic/ThematicDialog.js b/src/components/edit/thematic/ThematicDialog.js index 978e6022a..88bb09a78 100644 --- a/src/components/edit/thematic/ThematicDialog.js +++ b/src/components/edit/thematic/ThematicDialog.js @@ -305,7 +305,6 @@ class ThematicDialog extends Component { calculationError, eventDataItemError, programIndicatorError, - // periodTypeError, periodError, orgUnitsError, legendSetError, diff --git a/src/components/interpretations/InterpretationsToggle.js b/src/components/interpretations/InterpretationsToggle.js index a47cce286..d5b43ff48 100644 --- a/src/components/interpretations/InterpretationsToggle.js +++ b/src/components/interpretations/InterpretationsToggle.js @@ -5,6 +5,7 @@ import { openInterpretationsPanel, closeInterpretationsPanel, } from '../../actions/ui.js' +import useKeyDown from '../../hooks/useKeyDown.js' const InterpretationsToggle = () => { const interpretationsEnabled = useSelector((state) => Boolean(state.map.id)) @@ -21,6 +22,14 @@ const InterpretationsToggle = () => { } }, [dispatch, interpretationsOpen]) + const onClose = useCallback(() => { + if (interpretationsOpen) { + dispatch(closeInterpretationsPanel()) + } + }, [dispatch, interpretationsOpen]) + + useKeyDown('Escape', onClose, true) + return ( { const { map, isPlugin } = context const container = useMemo(() => document.createElement('div'), []) + useKeyDown('Escape', () => map.closePopup()) + // Create and open popup on map useEffect(() => { container.className = className diff --git a/src/components/orgunits/OrgUnitProfile.js b/src/components/orgunits/OrgUnitProfile.js index 099908e8d..669e53191 100644 --- a/src/components/orgunits/OrgUnitProfile.js +++ b/src/components/orgunits/OrgUnitProfile.js @@ -4,6 +4,7 @@ import { CenteredContent, CircularLoader, IconCross24 } from '@dhis2/ui' import React, { useEffect } from 'react' import { useSelector, useDispatch } from 'react-redux' import { closeOrgUnitProfile } from '../../actions/orgUnits.js' +import useKeyDown from '../../hooks/useKeyDown.js' import Drawer from '../core/Drawer.js' import OrgUnitData from './OrgUnitData.js' import OrgUnitInfo from './OrgUnitInfo.js' @@ -34,6 +35,8 @@ const OrgUnitProfile = () => { } }, [id, refetch]) + useKeyDown('Escape', () => dispatch(closeOrgUnitProfile())) + if (!id) { return null } diff --git a/src/hooks/useKeyDown.js b/src/hooks/useKeyDown.js index 52bc9ae23..a17499b75 100644 --- a/src/hooks/useKeyDown.js +++ b/src/hooks/useKeyDown.js @@ -1,18 +1,42 @@ -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' + +const useKeyDown = (key, callback, longPress = false) => { + const timerRef = useRef(null) -const useKeyDown = (key, callback) => { useEffect(() => { const handleKeyDown = (event) => { if (event.key === key) { - callback(event) + if (!longPress) { + callback(event) + } else { + // Start a timer for detecting long press + timerRef.current = setTimeout(() => { + callback(event) + }, 250) // Adjust delay for long press detection + } + } + } + + const handleKeyUp = (event) => { + if (event.key === key && longPress) { + // Clear the timer if the key is released before the delay + clearTimeout(timerRef.current) } } window.addEventListener('keydown', handleKeyDown) + window.addEventListener('keyup', handleKeyUp) + return () => { window.removeEventListener('keydown', handleKeyDown) + window.removeEventListener('keyup', handleKeyUp) } - }, [key, callback]) + }, [key, callback, longPress]) + + useEffect(() => { + // Cleanup on unmount + return () => clearTimeout(timerRef.current) + }, []) } export default useKeyDown