mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 05:37:32 +00:00
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:
@@ -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({
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user