refactor(core): replace the Modal of the FindInPage component with Dialog (#7149)

In order to customize the opening and closing styles, the Modal were replaced.

https://github.com/toeverything/AFFiNE/assets/102217452/ece774ea-634c-4723-bace-7926f003497c
This commit is contained in:
JimmFly
2024-06-18 07:46:22 +00:00
parent b3ec3a2b3e
commit ea718d30e9
2 changed files with 149 additions and 77 deletions

View File

@@ -1,17 +1,73 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';
import { createVar, keyframes, style } from '@vanilla-extract/css';
export const container = style({
export const animationTimeout = createVar();
const contentShow = keyframes({
from: {
opacity: 0,
transform: 'translateY(-2%) scale(0.96)',
},
to: {
opacity: 1,
transform: 'translateY(0) scale(1)',
},
});
const contentHide = keyframes({
to: {
opacity: 0,
transform: 'translateY(-2%) scale(0.96)',
},
from: {
opacity: 1,
transform: 'translateY(0) scale(1)',
},
});
export const modalOverlay = style({
position: 'fixed',
inset: 0,
backgroundColor: 'transparent',
zIndex: cssVar('zIndexModal'),
});
export const modalContentWrapper = style({
position: 'fixed',
inset: 0,
display: 'flex',
alignItems: 'flex-start',
justifyContent: 'flex-end',
zIndex: cssVar('zIndexModal'),
right: '28px',
top: '80px',
});
export const modalContent = style({
width: 400,
height: 48,
backgroundColor: cssVar('backgroundOverlayPanelColor'),
borderRadius: '8px',
boxShadow: cssVar('shadow3'),
minHeight: 48,
// :focus-visible will set outline
outline: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '8px 12px 8px 8px',
position: 'fixed',
right: '28px',
top: '80px',
borderRadius: '8px',
boxShadow: cssVar('shadow3'),
border: `0.5px solid ${cssVar('borderColor')}`,
padding: '8px 12px 8px 8px',
zIndex: cssVar('zIndexModal'),
willChange: 'transform, opacity',
selectors: {
'&[data-state=entered], &[data-state=entering]': {
animation: `${contentShow} ${animationTimeout} cubic-bezier(0.42, 0, 0.58, 1)`,
animationFillMode: 'forwards',
},
'&[data-state=exited], &[data-state=exiting]': {
animation: `${contentHide} ${animationTimeout} cubic-bezier(0.42, 0, 0.58, 1)`,
animationFillMode: 'forwards',
},
},
});
export const leftContent = style({

View File

@@ -1,11 +1,13 @@
import { Button, IconButton, Modal } from '@affine/component';
import { Button, IconButton } from '@affine/component';
import {
ArrowDownSmallIcon,
ArrowUpSmallIcon,
CloseIcon,
SearchIcon,
} from '@blocksuite/icons';
import * as Dialog from '@radix-ui/react-dialog';
import { useLiveData, useService } from '@toeverything/infra';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import {
type KeyboardEventHandler,
@@ -14,10 +16,13 @@ import {
useRef,
useState,
} from 'react';
import { useTransition } from 'react-transition-state';
import { FindInPageService } from '../services/find-in-page';
import * as styles from './find-in-page-modal.css';
const animationTimeout = 120;
const drawText = (canvas: HTMLCanvasElement, text: string) => {
const ctx = canvas.getContext('2d');
if (!ctx) {
@@ -81,6 +86,14 @@ export const FindInPageModal = () => {
const inputRef = useRef<HTMLInputElement>(null);
const [active, setActive] = useState(false);
const [{ status }, toggle] = useTransition({
timeout: animationTimeout,
});
useEffect(() => {
toggle(visible);
}, [visible]);
const handleValueChange = useCallback(
(v: string) => {
setValue(v);
@@ -164,74 +177,77 @@ export const FindInPageModal = () => {
);
return (
<Modal
open={visible}
modal={false}
withoutCloseButton
width={400}
height={48}
minHeight={48}
contentOptions={{
className: styles.container,
}}
>
<div className={styles.leftContent}>
<div
className={clsx(styles.inputContainer, {
active: active,
})}
>
<SearchIcon className={styles.searchIcon} />
<div className={styles.inputMain}>
<input
type="text"
autoFocus
value={value}
ref={inputRef}
style={{
visibility: isSearching ? 'hidden' : 'visible',
}}
onBlur={handleBlur}
onFocus={handleFocus}
className={styles.input}
onKeyDown={handleKeydown}
onChange={e => handleValueChange(e.target.value)}
/>
<CanvasText className={styles.inputHack} text={value} />
</div>
<div className={styles.count}>
{value.length > 0 && result && result.matches !== 0 ? (
<>
<span>{result?.activeMatchOrdinal || 0}</span>
<span>/</span>
<span>{result?.matches || 0}</span>
</>
) : value.length ? (
<span>No matches</span>
) : null}
</div>
</div>
<Dialog.Root modal open={status !== 'exited'}>
<Dialog.Portal>
<Dialog.Overlay className={styles.modalOverlay} />
<div className={styles.modalContentWrapper}>
<Dialog.Content
style={assignInlineVars({
[styles.animationTimeout]: `${animationTimeout}ms`,
})}
className={styles.modalContent}
data-state={status}
>
<div className={styles.leftContent}>
<div
className={clsx(styles.inputContainer, {
active: active,
})}
>
<SearchIcon className={styles.searchIcon} />
<div className={styles.inputMain}>
<input
type="text"
autoFocus
value={value}
ref={inputRef}
style={{
visibility: isSearching ? 'hidden' : 'visible',
}}
onBlur={handleBlur}
onFocus={handleFocus}
className={styles.input}
onKeyDown={handleKeydown}
onChange={e => handleValueChange(e.target.value)}
/>
<CanvasText className={styles.inputHack} text={value} />
</div>
<div className={styles.count}>
{value.length > 0 && result && result.matches !== 0 ? (
<>
<span>{result?.activeMatchOrdinal || 0}</span>
<span>/</span>
<span>{result?.matches || 0}</span>
</>
) : value.length ? (
<span>No matches</span>
) : null}
</div>
</div>
<Button
className={clsx(styles.arrowButton, 'backward')}
onClick={handleBackWard}
>
<ArrowUpSmallIcon />
</Button>
<Button
className={clsx(styles.arrowButton, 'forward')}
onClick={handleForward}
>
<ArrowDownSmallIcon />
</Button>
</div>
<IconButton
className={styles.closeButton}
type="plain"
onClick={handleDone}
>
<CloseIcon />
</IconButton>
</Modal>
<Button
className={clsx(styles.arrowButton, 'backward')}
onClick={handleBackWard}
>
<ArrowUpSmallIcon />
</Button>
<Button
className={clsx(styles.arrowButton, 'forward')}
onClick={handleForward}
>
<ArrowDownSmallIcon />
</Button>
</div>
<IconButton
className={styles.closeButton}
type="plain"
onClick={handleDone}
>
<CloseIcon />
</IconButton>
</Dialog.Content>
</div>
</Dialog.Portal>
</Dialog.Root>
);
};