mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 05:37:32 +00:00
feat(component): add animations to modal (#7474)
Add opening and closing animations to modal.
The usage of conditional rendering as shown below is not recommended:
```
open ? (
<Modal
open={open}
...
/>
) : null,
```
When the modal is closed, it gets removed from the DOM instantly without running any exit animations that might be defined in the Modal component.
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import { notify } from '@affine/component';
|
||||
import { AuthInput, ModalHeader } from '@affine/component/auth-components';
|
||||
import { Button } from '@affine/component/ui/button';
|
||||
import { authAtom } from '@affine/core/atoms';
|
||||
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
|
||||
import { Trans, useI18n } from '@affine/i18n';
|
||||
import { ArrowDownBigIcon } from '@blocksuite/icons/rc';
|
||||
import { useLiveData, useService } from '@toeverything/infra';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import type { FC } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
@@ -34,6 +36,7 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
const [verifyToken, challenge] = useCaptcha();
|
||||
|
||||
const [isValidEmail, setIsValidEmail] = useState(true);
|
||||
const { openModal } = useAtomValue(authAtom);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = setInterval(() => {
|
||||
@@ -45,7 +48,7 @@ export const SignIn: FC<AuthPanelProps> = ({
|
||||
};
|
||||
}, [authService]);
|
||||
const loginStatus = useLiveData(authService.session.status$);
|
||||
if (loginStatus === 'authenticated') {
|
||||
if (loginStatus === 'authenticated' && openModal) {
|
||||
onSignedIn?.();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,46 +1,13 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import { createVar, keyframes, style } from '@vanilla-extract/css';
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const animationTimeout = createVar();
|
||||
|
||||
const contentShow = keyframes({
|
||||
from: {
|
||||
opacity: 0,
|
||||
},
|
||||
to: {
|
||||
opacity: 1,
|
||||
},
|
||||
export const container = style({
|
||||
maxWidth: 480,
|
||||
minWidth: 360,
|
||||
padding: '20px 0',
|
||||
alignSelf: 'start',
|
||||
marginTop: '120px',
|
||||
});
|
||||
const contentHide = keyframes({
|
||||
to: {
|
||||
opacity: 0,
|
||||
},
|
||||
from: {
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const overlay = style({
|
||||
selectors: {
|
||||
'&.entered, &.entering': {
|
||||
animation: `${contentShow} ${animationTimeout} forwards`,
|
||||
},
|
||||
'&.exited, &.exiting': {
|
||||
animation: `${contentHide} ${animationTimeout} forwards`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const container = style([
|
||||
overlay,
|
||||
{
|
||||
maxWidth: 480,
|
||||
minWidth: 360,
|
||||
padding: '20px 0',
|
||||
alignSelf: 'start',
|
||||
marginTop: '120px',
|
||||
},
|
||||
]);
|
||||
|
||||
export const titleContainer = style({
|
||||
display: 'flex',
|
||||
|
||||
@@ -12,17 +12,7 @@ import {
|
||||
useService,
|
||||
type Workspace,
|
||||
} from '@toeverything/infra';
|
||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
Suspense,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useTransition } from 'react-transition-state';
|
||||
import { Suspense, useCallback, useContext, useMemo, useRef } from 'react';
|
||||
|
||||
import { BlocksuiteHeaderTitle } from '../../../blocksuite/block-suite-header/title';
|
||||
import { managerContext } from '../common';
|
||||
@@ -37,8 +27,6 @@ import * as styles from './info-modal.css';
|
||||
import { TagsRow } from './tags-row';
|
||||
import { TimeRow } from './time-row';
|
||||
|
||||
const animationTimeout = 120;
|
||||
|
||||
export const InfoModal = ({
|
||||
open,
|
||||
onOpenChange,
|
||||
@@ -52,14 +40,6 @@ export const InfoModal = ({
|
||||
}) => {
|
||||
const titleInputHandleRef = useRef<InlineEditHandle>(null);
|
||||
|
||||
const [{ status }, toggle] = useTransition({
|
||||
timeout: animationTimeout,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
toggle(open);
|
||||
}, [open, toggle]);
|
||||
|
||||
const manager = usePagePropertiesManager(page);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
@@ -80,20 +60,10 @@ export const InfoModal = ({
|
||||
|
||||
return (
|
||||
<Modal
|
||||
overlayOptions={{
|
||||
className: clsx(styles.overlay, status),
|
||||
style: assignInlineVars({
|
||||
[styles.animationTimeout]: `${animationTimeout}ms`,
|
||||
}),
|
||||
}}
|
||||
contentOptions={{
|
||||
className: clsx(styles.container, status),
|
||||
'aria-describedby': undefined,
|
||||
style: assignInlineVars({
|
||||
[styles.animationTimeout]: `${animationTimeout}ms`,
|
||||
}),
|
||||
className: styles.container,
|
||||
}}
|
||||
open={status !== 'exited'}
|
||||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
withoutCloseButton
|
||||
>
|
||||
|
||||
@@ -23,9 +23,6 @@ export interface EditCollectionModalProps {
|
||||
}
|
||||
|
||||
const contentOptions: DialogContentProps = {
|
||||
onPointerDownOutside: e => {
|
||||
e.preventDefault();
|
||||
},
|
||||
style: {
|
||||
padding: 0,
|
||||
maxWidth: 944,
|
||||
@@ -60,6 +57,7 @@ export const EditCollectionModal = ({
|
||||
width="calc(100% - 64px)"
|
||||
height="80%"
|
||||
contentOptions={contentOptions}
|
||||
persistent
|
||||
>
|
||||
{open && init ? (
|
||||
<EditCollection
|
||||
|
||||
@@ -12,9 +12,14 @@ export const useSelectPage = ({
|
||||
init: string[];
|
||||
onConfirm: (ids: string[]) => void;
|
||||
}>();
|
||||
const close = useCallback(() => {
|
||||
onChange(undefined);
|
||||
const close = useCallback((open: boolean) => {
|
||||
if (!open) {
|
||||
onChange(undefined);
|
||||
}
|
||||
}, []);
|
||||
const handleCancel = useCallback(() => {
|
||||
close(false);
|
||||
}, [close]);
|
||||
return {
|
||||
node: (
|
||||
<Modal
|
||||
@@ -38,7 +43,7 @@ export const useSelectPage = ({
|
||||
allPageListConfig={allPageListConfig}
|
||||
init={value.init}
|
||||
onConfirm={value.onConfirm}
|
||||
onCancel={close}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
) : null}
|
||||
</Modal>
|
||||
@@ -48,7 +53,7 @@ export const useSelectPage = ({
|
||||
onChange({
|
||||
init,
|
||||
onConfirm: list => {
|
||||
close();
|
||||
close(false);
|
||||
res(list);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,18 +11,22 @@ export const useEditCollection = () => {
|
||||
mode?: 'page' | 'rule';
|
||||
onConfirm: (collection: Collection) => void;
|
||||
}>();
|
||||
const close = useCallback(() => setData(undefined), []);
|
||||
const close = useCallback((open: boolean) => {
|
||||
if (!open) {
|
||||
setData(undefined);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
node: data ? (
|
||||
node: (
|
||||
<EditCollectionModal
|
||||
init={data.collection}
|
||||
init={data?.collection}
|
||||
open={!!data}
|
||||
mode={data.mode}
|
||||
mode={data?.mode}
|
||||
onOpenChange={close}
|
||||
onConfirm={data.onConfirm}
|
||||
onConfirm={data?.onConfirm ?? (() => {})}
|
||||
/>
|
||||
) : null,
|
||||
),
|
||||
open: (
|
||||
collection: Collection,
|
||||
mode?: EditCollectionMode
|
||||
@@ -50,19 +54,23 @@ export const useEditCollectionName = ({
|
||||
name: string;
|
||||
onConfirm: (name: string) => void;
|
||||
}>();
|
||||
const close = useCallback(() => setData(undefined), []);
|
||||
const close = useCallback((open: boolean) => {
|
||||
if (!open) {
|
||||
setData(undefined);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
node: data ? (
|
||||
node: (
|
||||
<CreateCollectionModal
|
||||
showTips={showTips}
|
||||
title={title}
|
||||
init={data.name}
|
||||
init={data?.name ?? ''}
|
||||
open={!!data}
|
||||
onOpenChange={close}
|
||||
onConfirm={data.onConfirm}
|
||||
onConfirm={data?.onConfirm ?? (() => {})}
|
||||
/>
|
||||
) : null,
|
||||
),
|
||||
open: (name: string): Promise<string> =>
|
||||
new Promise<string>(res => {
|
||||
setData({
|
||||
|
||||
Reference in New Issue
Block a user