From edff69b2f66d795cd085d07d1011155f2d34ba4b Mon Sep 17 00:00:00 2001 From: Weiko Date: Fri, 25 Aug 2023 13:59:04 +0200 Subject: [PATCH] Add hotkeys to modals (#1305) * Add hotkeys to modals * fix * fix * remove unnecessary type * restore type * add handleEnter * rename event props --- .../ui/modal/components/ConfirmationModal.tsx | 9 +-- .../src/modules/ui/modal/components/Modal.tsx | 58 ++++++++++++++++--- .../components/types/ModalHotkeyScope.ts | 3 + 3 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 front/src/modules/ui/modal/components/types/ModalHotkeyScope.ts diff --git a/front/src/modules/ui/modal/components/ConfirmationModal.tsx b/front/src/modules/ui/modal/components/ConfirmationModal.tsx index 39d68db70..42cf9958b 100644 --- a/front/src/modules/ui/modal/components/ConfirmationModal.tsx +++ b/front/src/modules/ui/modal/components/ConfirmationModal.tsx @@ -60,10 +60,10 @@ export function ConfirmationModal({ const handleInputConfimrationValueChange = (value: string) => { setInputConfirmationValue(value); - isValueMatchingUserEmail(confirmationValue, value); + isValueMatchingInput(confirmationValue, value); }; - const isValueMatchingUserEmail = debounce( + const isValueMatchingInput = debounce( (value?: string, inputValue?: string) => { setIsValidValue(Boolean(value && inputValue && value === inputValue)); }, @@ -75,11 +75,12 @@ export function ConfirmationModal({ { + onClose={() => { if (isOpen) { setIsOpen(false); } }} + onEnter={onConfirmClick} >
)} diff --git a/front/src/modules/ui/modal/components/Modal.tsx b/front/src/modules/ui/modal/components/Modal.tsx index 490376224..204c72520 100644 --- a/front/src/modules/ui/modal/components/Modal.tsx +++ b/front/src/modules/ui/modal/components/Modal.tsx @@ -1,12 +1,17 @@ -import React, { useRef } from 'react'; +import React, { useEffect, useRef } from 'react'; import styled from '@emotion/styled'; import { motion } from 'framer-motion'; +import { Key } from 'ts-key-enum'; +import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; +import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { ClickOutsideMode, useListenClickOutside, } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; +import { ModalHotkeyScope } from './types/ModalHotkeyScope'; + const StyledModalDiv = styled(motion.div)` display: flex; flex-direction: column; @@ -83,7 +88,9 @@ function ModalFooter({ children, ...restProps }: ModalFooterProps) { type ModalProps = React.PropsWithChildren & React.ComponentProps<'div'> & { isOpen?: boolean; - onOutsideClick?: () => void; + onClose?: () => void; + hotkeyScope?: ModalHotkeyScope; + onEnter?: () => void; }; const modalVariants = { @@ -95,22 +102,55 @@ const modalVariants = { export function Modal({ isOpen = false, children, - onOutsideClick, + onClose, + hotkeyScope = ModalHotkeyScope.Default, + onEnter, ...restProps }: ModalProps) { const modalRef = useRef(null); useListenClickOutside({ refs: [modalRef], - callback: () => onOutsideClick?.(), + callback: () => onClose?.(), mode: ClickOutsideMode.absolute, }); - if (!isOpen) { - return null; - } + const { + goBackToPreviousHotkeyScope, + setHotkeyScopeAndMemorizePreviousScope, + } = usePreviousHotkeyScope(); - return ( + useScopedHotkeys( + [Key.Escape], + () => { + onClose?.(); + }, + hotkeyScope, + [onClose], + ); + + useScopedHotkeys( + [Key.Enter], + () => { + onEnter?.(); + }, + hotkeyScope, + ); + + useEffect(() => { + if (isOpen) { + setHotkeyScopeAndMemorizePreviousScope(hotkeyScope); + } else { + goBackToPreviousHotkeyScope(); + } + }, [ + goBackToPreviousHotkeyScope, + hotkeyScope, + isOpen, + setHotkeyScopeAndMemorizePreviousScope, + ]); + + return isOpen ? ( + ) : ( + <> ); } diff --git a/front/src/modules/ui/modal/components/types/ModalHotkeyScope.ts b/front/src/modules/ui/modal/components/types/ModalHotkeyScope.ts new file mode 100644 index 000000000..e7ee9a502 --- /dev/null +++ b/front/src/modules/ui/modal/components/types/ModalHotkeyScope.ts @@ -0,0 +1,3 @@ +export enum ModalHotkeyScope { + Default = 'default', +}