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 { 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', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', 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')}`, 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({ export const leftContent = style({

View File

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