'use client'; /* eslint-disable jsdoc/require-jsdoc */ import { type ComponentType, type KeyboardEvent, type ReactNode, useCallback } from 'react'; import { classNames, noop } from '@vkontakte/vkjs'; import { useAdaptivityWithJSMediaQueries } from '../../hooks/useAdaptivityWithJSMediaQueries'; import { useExternRef } from '../../hooks/useExternRef'; import { useFocusTrap } from '../../hooks/useFocusTrap'; import { usePlatform } from '../../hooks/usePlatform'; import { useVirtualKeyboardState } from '../../hooks/useVirtualKeyboardState'; import { Keys, pressedKey } from '../../lib/accessibility'; import { useCSSTransition, type UseCSSTransitionState } from '../../lib/animation'; import { useBottomSheet } from '../../lib/sheet'; import { useScrollLock } from '../AppRoot/ScrollContext'; import { ModalCardBase } from '../ModalCardBase/ModalCardBase'; import { ModalOutlet } from '../ModalOutlet/ModalOutlet'; import { ModalOverlay as ModalOverlayDefault, type ModalOverlayProps, } from '../ModalOverlay/ModalOverlay'; import type { ModalCardProps } from './types'; import styles from './ModalCard.module.css'; const sizeByPlatformClassNames = { vkcom: styles['hostMaxWidthS'], ios: styles['hostMaxWidthM'], android: styles['hostMaxWidthL'], }; const transitionStateClassNames: Partial> = { appear: styles['hostStateEnter'], appearing: styles['hostStateEntering'], enter: styles['hostStateEnter'], entering: styles['hostStateEntering'], exiting: styles['hostStateExiting'], exited: styles['hostStateExited'], }; export interface ModalCardInternalProps extends Omit { ModalOverlay?: ComponentType; } /** * В компоненте заложена вся логика модального окна. * * @private */ export const ModalCardInternal = ({ icon, title, titleComponent, description, descriptionComponent, children, actions, size, open, style: styleProp, className, preventClose, ModalOverlay = ModalOverlayDefault, modalOverlayTestId, modalDismissButtonTestId, getRootRef, dismissButtonMode, dismissLabel, noFocusToDialog, restoreFocus, onOpen, onOpened, onClose = noop, onClosed, disableFocusTrap, disableModalOverlay, disableOpenAnimation, disableCloseAnimation, ...restProps }: ModalCardInternalProps): ReactNode => { const platform = usePlatform(); const [transitionState, { ref, onTransitionEnd }] = useCSSTransition(open, { enableAppear: !disableOpenAnimation, enableEnter: !disableOpenAnimation, enableExit: !disableCloseAnimation, onEnter() { onOpen?.(); }, onEntered() { onOpened?.(); }, onExited() { onClosed?.(); }, }); const opened = transitionState === 'appeared' || transitionState === 'entered'; const hidden = transitionState === 'exited'; const closable = !preventClose && opened; const { isDesktop } = useAdaptivityWithJSMediaQueries(); const bottomSheetEnabled = !isDesktop && !preventClose && transitionState !== 'exited'; const { opened: keyboardOpened } = useVirtualKeyboardState(bottomSheetEnabled); const [{ setSheetEl, setBackdropEl }, bottomSheetEventHandlers] = useBottomSheet( bottomSheetEnabled, { blocked: keyboardOpened, snapPoint: 'auto', sheetCSSProperty: '--vkui_internal_ModalCard--translateY', backdropCSSProperty: '--vkui_internal--modal-overlay--opacity', onDismiss() { onClose?.('swipe-down'); }, }, ); const handleRef = useExternRef(setSheetEl, ref, getRootRef); const style = keyboardOpened ? { ...styleProp, '--vkui_internal_ModalCard--safeAreaInsetBottom': '0px', } : styleProp; const modalOverlay = !disableModalOverlay && ( ); const handleEscKeyDown = useCallback( (event: KeyboardEvent) => { if (closable && pressedKey(event) === Keys.ESCAPE) { onClose('escape-key'); } }, [closable, onClose], ); useScrollLock(!hidden); useFocusTrap(ref, { autoFocus: !noFocusToDialog, disabled: !opened || hidden || disableFocusTrap, restoreFocus, }); return ( ); };