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) => { const handleInputConfimrationValueChange = (value: string) => {
setInputConfirmationValue(value); setInputConfirmationValue(value);
isValueMatchingUserEmail(confirmationValue, value); isValueMatchingInput(confirmationValue, value);
}; };
const isValueMatchingUserEmail = debounce( const isValueMatchingInput = debounce(
(value?: string, inputValue?: string) => { (value?: string, inputValue?: string) => {
setIsValidValue(Boolean(value && inputValue && value === inputValue)); setIsValidValue(Boolean(value && inputValue && value === inputValue));
}, },
@ -75,11 +75,12 @@ export function ConfirmationModal({
<LayoutGroup> <LayoutGroup>
<StyledConfirmationModal <StyledConfirmationModal
isOpen={isOpen} isOpen={isOpen}
onOutsideClick={() => { onClose={() => {
if (isOpen) { if (isOpen) {
setIsOpen(false); setIsOpen(false);
} }
}} }}
onEnter={onConfirmClick}
> >
<H1Title title={title} fontColor={H1TitleFontColor.Primary} /> <H1Title title={title} fontColor={H1TitleFontColor.Primary} />
<Section <Section
@ -95,7 +96,7 @@ export function ConfirmationModal({
onChange={handleInputConfimrationValueChange} onChange={handleInputConfimrationValueChange}
placeholder={confirmationPlaceholder} placeholder={confirmationPlaceholder}
fullWidth fullWidth
key={'email-' + confirmationValue} key={'input-' + confirmationValue}
/> />
</Section> </Section>
)} )}

View File

@ -1,12 +1,17 @@
import React, { useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { motion } from 'framer-motion'; 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 { import {
ClickOutsideMode, ClickOutsideMode,
useListenClickOutside, useListenClickOutside,
} from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { ModalHotkeyScope } from './types/ModalHotkeyScope';
const StyledModalDiv = styled(motion.div)` const StyledModalDiv = styled(motion.div)`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -83,7 +88,9 @@ function ModalFooter({ children, ...restProps }: ModalFooterProps) {
type ModalProps = React.PropsWithChildren & type ModalProps = React.PropsWithChildren &
React.ComponentProps<'div'> & { React.ComponentProps<'div'> & {
isOpen?: boolean; isOpen?: boolean;
onOutsideClick?: () => void; onClose?: () => void;
hotkeyScope?: ModalHotkeyScope;
onEnter?: () => void;
}; };
const modalVariants = { const modalVariants = {
@ -95,22 +102,55 @@ const modalVariants = {
export function Modal({ export function Modal({
isOpen = false, isOpen = false,
children, children,
onOutsideClick, onClose,
hotkeyScope = ModalHotkeyScope.Default,
onEnter,
...restProps ...restProps
}: ModalProps) { }: ModalProps) {
const modalRef = useRef<HTMLDivElement>(null); const modalRef = useRef<HTMLDivElement>(null);
useListenClickOutside({ useListenClickOutside({
refs: [modalRef], refs: [modalRef],
callback: () => onOutsideClick?.(), callback: () => onClose?.(),
mode: ClickOutsideMode.absolute, mode: ClickOutsideMode.absolute,
}); });
if (!isOpen) { const {
return null; 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> <StyledBackDrop>
<StyledModalDiv <StyledModalDiv
// framer-motion seems to have typing problems with refs // framer-motion seems to have typing problems with refs
@ -127,6 +167,8 @@ export function Modal({
{children} {children}
</StyledModalDiv> </StyledModalDiv>
</StyledBackDrop> </StyledBackDrop>
) : (
<></>
); );
} }

View File

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