Add hotkeys to modals (#1305)

* Add hotkeys to modals

* fix

* fix

* remove unnecessary type

* restore type

* add handleEnter

* rename event props
This commit is contained in:
Weiko
2023-08-25 13:59:04 +02:00
committed by GitHub
parent 69e0917338
commit edff69b2f6
3 changed files with 58 additions and 12 deletions

View File

@ -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({
<LayoutGroup>
<StyledConfirmationModal
isOpen={isOpen}
onOutsideClick={() => {
onClose={() => {
if (isOpen) {
setIsOpen(false);
}
}}
onEnter={onConfirmClick}
>
<H1Title title={title} fontColor={H1TitleFontColor.Primary} />
<Section
@ -95,7 +96,7 @@ export function ConfirmationModal({
onChange={handleInputConfimrationValueChange}
placeholder={confirmationPlaceholder}
fullWidth
key={'email-' + confirmationValue}
key={'input-' + confirmationValue}
/>
</Section>
)}

View File

@ -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<HTMLDivElement>(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 ? (
<StyledBackDrop>
<StyledModalDiv
// framer-motion seems to have typing problems with refs
@ -127,6 +167,8 @@ export function Modal({
{children}
</StyledModalDiv>
</StyledBackDrop>
) : (
<></>
);
}

View File

@ -0,0 +1,3 @@
export enum ModalHotkeyScope {
Default = 'default',
}