Adds open/close animations to dialogs and modals

This commit is contained in:
Josh Perez 2021-09-29 16:59:37 -04:00 committed by GitHub
parent fc066e05df
commit b6cfe0933d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 635 additions and 224 deletions

View file

@ -1,7 +1,7 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useRef, useState, ReactElement, ReactNode } from 'react';
import React, { ReactElement, ReactNode, useRef, useState } from 'react';
import Measure, { ContentRect, MeasuredComponentProps } from 'react-measure';
import classNames from 'classnames';
import { noop } from 'lodash';
@ -10,6 +10,7 @@ import { LocalizerType } from '../types/Util';
import { ModalHost } from './ModalHost';
import { Theme } from '../util/theme';
import { getClassNamesFor } from '../util/getClassNamesFor';
import { useAnimated } from '../hooks/useAnimated';
import { useHasWrapped } from '../hooks/useHasWrapped';
type PropsType = {
@ -18,9 +19,12 @@ type PropsType = {
hasXButton?: boolean;
i18n: LocalizerType;
moduleClassName?: string;
noMouseClose?: boolean;
onClose?: () => void;
title?: ReactNode;
};
type ModalPropsType = PropsType & {
noMouseClose?: boolean;
theme?: Theme;
};
@ -36,8 +40,51 @@ export function Modal({
onClose = noop,
title,
theme,
}: Readonly<PropsType>): ReactElement {
}: Readonly<ModalPropsType>): ReactElement {
const { close, renderAnimation } = useAnimated(
{
from: { opacity: 0, transform: 'translateY(48px)' },
enter: { opacity: 1, transform: 'translateY(0px)' },
leave: {
opacity: 0,
transform: 'translateY(48px)',
},
config: {
duration: 200,
},
},
onClose
);
return (
<ModalHost noMouseClose={noMouseClose} onClose={close} theme={theme}>
{renderAnimation(
<ModalWindow
hasStickyButtons={hasStickyButtons}
hasXButton={hasXButton}
i18n={i18n}
moduleClassName={moduleClassName}
onClose={close}
title={title}
>
{children}
</ModalWindow>
)}
</ModalHost>
);
}
export function ModalWindow({
children,
hasStickyButtons,
hasXButton,
i18n,
moduleClassName,
onClose = noop,
title,
}: Readonly<PropsType>): JSX.Element {
const modalRef = useRef<HTMLDivElement | null>(null);
const bodyRef = useRef<HTMLDivElement | null>(null);
const [scrolled, setScrolled] = useState(false);
const [hasOverflow, setHasOverflow] = useState(false);
@ -56,10 +103,10 @@ export function Modal({
}
return (
<ModalHost noMouseClose={noMouseClose} onClose={onClose} theme={theme}>
<>
{/* We don't want the click event to propagate to its container node. */}
{/* eslint-disable jsx-a11y/no-static-element-interactions */}
{/* eslint-disable jsx-a11y/click-events-have-key-events */}
{/* eslint-disable-next-line max-len */}
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */}
<div
className={classNames(
getClassName(''),
@ -71,8 +118,6 @@ export function Modal({
event.stopPropagation();
}}
>
{/* eslint-enable jsx-a11y/no-static-element-interactions */}
{/* eslint-enable jsx-a11y/click-events-have-key-events */}
{hasHeader && (
<div className={getClassName('__header')}>
{hasXButton && (
@ -81,9 +126,7 @@ export function Modal({
type="button"
className={getClassName('__close-button')}
tabIndex={0}
onClick={() => {
onClose();
}}
onClick={onClose}
/>
)}
{title && (
@ -122,7 +165,7 @@ export function Modal({
)}
</Measure>
</div>
</ModalHost>
</>
);
}