mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-02 14:33:54 +03:00
feat(core): adjust center peek animation (#7393)
This commit is contained in:
parent
7082f7ea7a
commit
242c41b440
@ -133,9 +133,17 @@ function resolvePeekInfoFromPeekTarget(
|
||||
return;
|
||||
}
|
||||
|
||||
export type PeekViewAnimation = 'fade' | 'zoom' | 'none';
|
||||
|
||||
export class PeekViewEntity extends Entity {
|
||||
private readonly _active$ = new LiveData<ActivePeekView | null>(null);
|
||||
private readonly _show$ = new LiveData<boolean>(false);
|
||||
private readonly _show$ = new LiveData<{
|
||||
animation: PeekViewAnimation;
|
||||
value: boolean;
|
||||
}>({
|
||||
animation: 'zoom',
|
||||
value: false,
|
||||
});
|
||||
|
||||
constructor(private readonly workbenchService: WorkbenchService) {
|
||||
super();
|
||||
@ -143,7 +151,7 @@ export class PeekViewEntity extends Entity {
|
||||
|
||||
active$ = this._active$.distinctUntilChanged();
|
||||
show$ = this._show$
|
||||
.map(show => show && this._active$.value !== null)
|
||||
.map(show => (this._active$.value !== null ? show : null))
|
||||
.distinctUntilChanged();
|
||||
|
||||
// return true if the peek view will be handled
|
||||
@ -159,17 +167,23 @@ export class PeekViewEntity extends Entity {
|
||||
const active = this._active$.value;
|
||||
|
||||
// if there is an active peek view and it is a doc peek view, we will navigate it first
|
||||
if (active?.info.type === 'doc' && this.show$.value) {
|
||||
if (active?.info.type === 'doc' && this.show$.value?.value) {
|
||||
// TODO(@pengx17): scroll to the viewing position?
|
||||
this.workbenchService.workbench.openDoc(active.info.docId);
|
||||
}
|
||||
|
||||
this._active$.next({ target, info: resolvedInfo });
|
||||
this._show$.next(true);
|
||||
this._show$.next({
|
||||
value: true,
|
||||
animation: resolvedInfo.type === 'doc' ? 'zoom' : 'fade',
|
||||
});
|
||||
return firstValueFrom(race(this._active$, this.show$).pipe(map(() => {})));
|
||||
};
|
||||
|
||||
close = () => {
|
||||
this._show$.next(false);
|
||||
close = (animation?: PeekViewAnimation) => {
|
||||
this._show$.next({
|
||||
value: false,
|
||||
animation: animation ?? this._show$.value.animation,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { OnEvent, Service } from '@toeverything/infra';
|
||||
import { WorkbenchLocationChanged } from '../../workbench/services/workbench';
|
||||
import { PeekViewEntity } from '../entities/peek-view';
|
||||
|
||||
@OnEvent(WorkbenchLocationChanged, e => e.peekView.close)
|
||||
@OnEvent(WorkbenchLocationChanged, e => () => e.peekView.close())
|
||||
export class PeekViewService extends Service {
|
||||
public readonly peekView = this.framework.createEntity(PeekViewEntity);
|
||||
}
|
||||
|
@ -451,7 +451,7 @@ export const ImagePreviewPeekView = (
|
||||
): ReactElement | null => {
|
||||
const [blockId, setBlockId] = useState<string | null>(props.blockId);
|
||||
const peekView = useService(PeekViewService).peekView;
|
||||
const onClose = peekView.close;
|
||||
const onClose = useCallback(() => peekView.close(), [peekView]);
|
||||
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -1,106 +1,17 @@
|
||||
import { cssVar } from '@toeverything/theme';
|
||||
import {
|
||||
createVar,
|
||||
generateIdentifier,
|
||||
globalStyle,
|
||||
keyframes,
|
||||
style,
|
||||
} from '@vanilla-extract/css';
|
||||
|
||||
export const animationTimeout = createVar();
|
||||
export const transformOrigin = createVar();
|
||||
export const animationType = createVar();
|
||||
|
||||
const zoomIn = keyframes({
|
||||
from: {
|
||||
transform: 'scale(0.10)',
|
||||
},
|
||||
to: {
|
||||
transform: 'scale(1)',
|
||||
},
|
||||
});
|
||||
const zoomOut = keyframes({
|
||||
to: {
|
||||
opacity: 0,
|
||||
transform: 'scale(0.10)',
|
||||
},
|
||||
from: {
|
||||
opacity: 1,
|
||||
transform: 'scale(1)',
|
||||
},
|
||||
});
|
||||
|
||||
const fadeIn = keyframes({
|
||||
from: {
|
||||
opacity: 0,
|
||||
},
|
||||
to: {
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
const fadeOut = keyframes({
|
||||
from: {
|
||||
opacity: 1,
|
||||
},
|
||||
to: {
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// every item must have its own unique view-transition-name
|
||||
const vtContentZoom = generateIdentifier('content-zoom');
|
||||
const vtContentFade = generateIdentifier('content-fade');
|
||||
const vtOverlayFade = generateIdentifier('content-fade');
|
||||
|
||||
globalStyle(`::view-transition-group(${vtOverlayFade})`, {
|
||||
animationDuration: animationTimeout,
|
||||
});
|
||||
|
||||
globalStyle(`::view-transition-new(${vtOverlayFade})`, {
|
||||
animationName: fadeIn,
|
||||
});
|
||||
|
||||
globalStyle(`::view-transition-old(${vtOverlayFade})`, {
|
||||
animationName: fadeOut,
|
||||
});
|
||||
|
||||
globalStyle(
|
||||
`::view-transition-group(${vtContentZoom}),
|
||||
::view-transition-group(${vtContentFade})`,
|
||||
{
|
||||
animationDuration: animationTimeout,
|
||||
animationFillMode: 'forwards',
|
||||
animationTimingFunction: 'cubic-bezier(0.42, 0, 0.58, 1)',
|
||||
}
|
||||
);
|
||||
|
||||
globalStyle(`::view-transition-new(${vtContentZoom})`, {
|
||||
animationName: zoomIn,
|
||||
// origin has to be set in ::view-transition-new/old
|
||||
transformOrigin: transformOrigin,
|
||||
});
|
||||
|
||||
globalStyle(`::view-transition-old(${vtContentZoom})`, {
|
||||
animationName: zoomOut,
|
||||
transformOrigin: transformOrigin,
|
||||
});
|
||||
|
||||
globalStyle(`::view-transition-new(${vtContentFade})`, {
|
||||
animationName: fadeIn,
|
||||
});
|
||||
|
||||
globalStyle(`::view-transition-old(${vtContentFade})`, {
|
||||
animationName: fadeOut,
|
||||
});
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const modalOverlay = style({
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
zIndex: cssVar('zIndexModal'),
|
||||
backgroundColor: cssVar('black30'),
|
||||
viewTransitionName: vtOverlayFade,
|
||||
pointerEvents: 'auto',
|
||||
selectors: {
|
||||
'&[data-anime-state="animating"]': {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const modalContentWrapper = style({
|
||||
@ -113,32 +24,36 @@ export const modalContentWrapper = style({
|
||||
});
|
||||
|
||||
export const modalContentContainer = style({
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: 12,
|
||||
selectors: {
|
||||
'[data-padding="true"] &': {
|
||||
width: '90%',
|
||||
height: '90%',
|
||||
maxWidth: 1248,
|
||||
},
|
||||
'&[data-anime-state="animating"]': {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const modalContentContainerWithZoom = style({
|
||||
viewTransitionName: vtContentZoom,
|
||||
});
|
||||
|
||||
export const modalContentContainerWithFade = style({
|
||||
viewTransitionName: vtContentFade,
|
||||
});
|
||||
|
||||
export const containerPadding = style({
|
||||
width: '90%',
|
||||
height: '90%',
|
||||
maxWidth: 1248,
|
||||
export const dialog = style({
|
||||
backgroundColor: cssVar('backgroundOverlayPanelColor'),
|
||||
boxShadow: cssVar('shadow3'),
|
||||
});
|
||||
|
||||
export const modalContent = style({
|
||||
flex: 1,
|
||||
borderRadius: 'inherit',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: cssVar('backgroundOverlayPanelColor'),
|
||||
backdropFilter: 'drop-shadow(0px 0px 2px rgba(0, 0, 0, 0.08))',
|
||||
borderRadius: '8px',
|
||||
flexShrink: 0,
|
||||
overflow: 'hidden',
|
||||
minHeight: 300,
|
||||
// :focus-visible will set outline
|
||||
outline: 'none',
|
||||
@ -151,7 +66,9 @@ export const modalContent = style({
|
||||
});
|
||||
|
||||
export const modalControls = style({
|
||||
flexShrink: 0,
|
||||
position: 'absolute',
|
||||
left: '100%',
|
||||
top: 0,
|
||||
zIndex: -1,
|
||||
minWidth: '48px',
|
||||
padding: '8px 0 0 16px',
|
||||
|
@ -1,19 +1,20 @@
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import anime, { type AnimeInstance, type AnimeParams } from 'animejs';
|
||||
import clsx from 'clsx';
|
||||
import {
|
||||
createContext,
|
||||
forwardRef,
|
||||
type PropsWithChildren,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { flushSync } from 'react-dom';
|
||||
|
||||
import type { PeekViewAnimation } from '../entities/peek-view';
|
||||
import * as styles from './modal-container.css';
|
||||
|
||||
const animationTimeout = 200;
|
||||
|
||||
const contentOptions: Dialog.DialogContentProps = {
|
||||
['data-testid' as string]: 'peek-view-modal',
|
||||
onPointerDownOutside: e => {
|
||||
@ -31,11 +32,6 @@ const contentOptions: Dialog.DialogContentProps = {
|
||||
// this is because radix-ui register the escape key event on the document using capture, which is not possible to prevent in blocksuite
|
||||
e.preventDefault();
|
||||
},
|
||||
style: {
|
||||
padding: 0,
|
||||
backgroundColor: 'transparent',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
};
|
||||
|
||||
// a dummy context to let elements know they are inside a peek view
|
||||
@ -50,31 +46,6 @@ export const useInsidePeekView = () => {
|
||||
return !!context;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert var(--xxx) to --xxx
|
||||
* @param fullName
|
||||
* @returns
|
||||
*/
|
||||
function toCssVarName(fullName: string) {
|
||||
return fullName.slice(4, -1);
|
||||
}
|
||||
|
||||
function getElementScreenPositionCenter(target: HTMLElement) {
|
||||
const rect = target.getBoundingClientRect();
|
||||
|
||||
if (rect.top === 0 || rect.left === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scrollTop = window.scrollY || document.documentElement.scrollTop;
|
||||
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
|
||||
|
||||
return {
|
||||
x: rect.x + scrollLeft + rect.width / 2,
|
||||
y: rect.y + scrollTop + rect.height / 2,
|
||||
};
|
||||
}
|
||||
|
||||
export type PeekViewModalContainerProps = PropsWithChildren<{
|
||||
onOpenChange: (open: boolean) => void;
|
||||
open: boolean;
|
||||
@ -83,8 +54,10 @@ export type PeekViewModalContainerProps = PropsWithChildren<{
|
||||
onAnimationStart?: () => void;
|
||||
onAnimateEnd?: () => void;
|
||||
padding?: boolean;
|
||||
animation?: 'fade' | 'zoom';
|
||||
animation?: PeekViewAnimation;
|
||||
testId?: string;
|
||||
/** Whether to apply shadow & bg */
|
||||
dialogFrame?: boolean;
|
||||
}>;
|
||||
|
||||
const PeekViewModalOverlay = 'div';
|
||||
@ -103,22 +76,193 @@ export const PeekViewModalContainer = forwardRef<
|
||||
onAnimateEnd,
|
||||
animation = 'zoom',
|
||||
padding = true,
|
||||
testId,
|
||||
dialogFrame = true,
|
||||
},
|
||||
ref
|
||||
) {
|
||||
const [vtOpen, setVtOpen] = useState(open);
|
||||
useEffect(() => {
|
||||
if (document.startViewTransition) {
|
||||
document.startViewTransition(() => {
|
||||
flushSync(() => {
|
||||
setVtOpen(open);
|
||||
const [animeState, setAnimeState] = useState<'idle' | 'ready' | 'animating'>(
|
||||
'idle'
|
||||
);
|
||||
const contentClipRef = useRef<HTMLDivElement>(null);
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const overlayRef = useRef<HTMLDivElement>(null);
|
||||
const controlsRef = useRef<HTMLDivElement>(null);
|
||||
const prevAnimeMap = useRef<Record<string, AnimeInstance | undefined>>({});
|
||||
|
||||
const animateControls = useCallback((animateIn = false) => {
|
||||
const controls = controlsRef.current;
|
||||
if (!controls) return;
|
||||
anime({
|
||||
targets: controls,
|
||||
opacity: animateIn ? [0, 1] : [1, 0],
|
||||
translateX: animateIn ? [-32, 0] : [0, -32],
|
||||
easing: 'easeOutQuad',
|
||||
duration: 230,
|
||||
});
|
||||
}, []);
|
||||
const zoomAnimate = useCallback(
|
||||
async (
|
||||
zoomIn?: boolean,
|
||||
paramsMap?: {
|
||||
overlay?: AnimeParams;
|
||||
content?: AnimeParams;
|
||||
contentWrapper?: AnimeParams;
|
||||
}
|
||||
) => {
|
||||
return new Promise<void>(resolve => {
|
||||
const contentClip = contentClipRef.current;
|
||||
const content = contentRef.current;
|
||||
const overlay = overlayRef.current;
|
||||
|
||||
if (!contentClip || !content || !target || !overlay) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
const targets = contentClip;
|
||||
const lockSizeEl = content;
|
||||
|
||||
const from = zoomIn ? target : contentClip;
|
||||
const to = zoomIn ? contentClip : target;
|
||||
|
||||
const fromRect = from.getBoundingClientRect();
|
||||
const toRect = to.getBoundingClientRect();
|
||||
|
||||
targets.style.position = 'fixed';
|
||||
targets.style.overflow = 'hidden';
|
||||
targets.style.opacity = '1';
|
||||
lockSizeEl.style.width = zoomIn
|
||||
? `${toRect.width}px`
|
||||
: `${fromRect.width}px`;
|
||||
lockSizeEl.style.height = zoomIn
|
||||
? `${toRect.height}px`
|
||||
: `${fromRect.height}px`;
|
||||
lockSizeEl.style.overflow = 'hidden';
|
||||
overlay.style.pointerEvents = 'none';
|
||||
|
||||
prevAnimeMap.current.overlay?.pause();
|
||||
prevAnimeMap.current.content?.pause();
|
||||
prevAnimeMap.current.contentWrapper?.pause();
|
||||
|
||||
const overlayAnime = anime({
|
||||
targets: overlay,
|
||||
opacity: zoomIn ? [0, 1] : [1, 0],
|
||||
easing: 'easeOutQuad',
|
||||
duration: 230,
|
||||
...paramsMap?.overlay,
|
||||
});
|
||||
|
||||
const contentAnime =
|
||||
paramsMap?.content &&
|
||||
anime({
|
||||
targets: content,
|
||||
...paramsMap.content,
|
||||
});
|
||||
|
||||
const contentWrapperAnime = anime({
|
||||
targets,
|
||||
left: [fromRect.left, toRect.left],
|
||||
top: [fromRect.top, toRect.top],
|
||||
width: [fromRect.width, toRect.width],
|
||||
height: [fromRect.height, toRect.height],
|
||||
easing: 'easeOutQuad',
|
||||
duration: 230,
|
||||
...paramsMap?.contentWrapper,
|
||||
complete: (ins: AnimeInstance) => {
|
||||
paramsMap?.contentWrapper?.complete?.(ins);
|
||||
setAnimeState('idle');
|
||||
overlay.style.pointerEvents = '';
|
||||
if (zoomIn) {
|
||||
Object.assign(targets.style, {
|
||||
position: '',
|
||||
left: '',
|
||||
top: '',
|
||||
width: '',
|
||||
height: '',
|
||||
overflow: '',
|
||||
});
|
||||
Object.assign(lockSizeEl.style, {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: '',
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
|
||||
prevAnimeMap.current = {
|
||||
overlay: overlayAnime,
|
||||
content: contentAnime,
|
||||
contentWrapper: contentWrapperAnime,
|
||||
};
|
||||
});
|
||||
},
|
||||
[target]
|
||||
);
|
||||
const animateZoomIn = useCallback(() => {
|
||||
setAnimeState('animating');
|
||||
setVtOpen(true);
|
||||
setTimeout(() => {
|
||||
zoomAnimate(true, {
|
||||
contentWrapper: {
|
||||
opacity: [0.5, 1],
|
||||
easing: 'cubicBezier(.46,.36,0,1)',
|
||||
duration: 400,
|
||||
},
|
||||
content: {
|
||||
opacity: [0, 1],
|
||||
duration: 100,
|
||||
},
|
||||
})
|
||||
.then(() => animateControls(true))
|
||||
.catch(console.error);
|
||||
}, 0);
|
||||
}, [animateControls, zoomAnimate]);
|
||||
const animateZoomOut = useCallback(() => {
|
||||
setAnimeState('animating');
|
||||
animateControls(false);
|
||||
zoomAnimate(false, {
|
||||
contentWrapper: {
|
||||
easing: 'cubicBezier(.57,.25,.76,.44)',
|
||||
opacity: [1, 0.5],
|
||||
duration: 180,
|
||||
},
|
||||
content: {
|
||||
opacity: [1, 0],
|
||||
duration: 180,
|
||||
easing: 'easeOutQuad',
|
||||
},
|
||||
})
|
||||
.then(() => setVtOpen(false))
|
||||
.catch(console.error);
|
||||
}, [animateControls, zoomAnimate]);
|
||||
|
||||
const animateFade = useCallback((animateIn: boolean) => {
|
||||
setAnimeState('animating');
|
||||
return new Promise<void>(resolve => {
|
||||
if (animateIn) setVtOpen(true);
|
||||
setTimeout(() => {
|
||||
const overlay = overlayRef.current;
|
||||
const contentClip = contentClipRef.current;
|
||||
if (!overlay || !contentClip) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
anime({
|
||||
targets: [overlay, contentClip],
|
||||
opacity: animateIn ? [0, 1] : [1, 0],
|
||||
easing: 'easeOutQuad',
|
||||
duration: 230,
|
||||
complete: () => {
|
||||
if (!animateIn) setVtOpen(false);
|
||||
setAnimeState('idle');
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setVtOpen(open);
|
||||
}
|
||||
}, [open]);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
@ -133,57 +277,53 @@ export const PeekViewModalContainer = forwardRef<
|
||||
}, [onOpenChange]);
|
||||
|
||||
useEffect(() => {
|
||||
const bondingBox = target ? getElementScreenPositionCenter(target) : null;
|
||||
const offsetLeft =
|
||||
(window.innerWidth - Math.min(window.innerWidth * 0.9, 1200)) / 2;
|
||||
const modalHeight = window.innerHeight * 0.05;
|
||||
const transformOrigin = bondingBox
|
||||
? `${bondingBox.x - offsetLeft}px ${bondingBox.y - modalHeight}px`
|
||||
: null;
|
||||
if (animation === 'zoom') {
|
||||
open ? animateZoomIn() : animateZoomOut();
|
||||
} else if (animation === 'fade') {
|
||||
animateFade(open).catch(console.error);
|
||||
} else if (animation === 'none') {
|
||||
setVtOpen(open);
|
||||
}
|
||||
}, [animateZoomOut, animation, open, target, animateZoomIn, animateFade]);
|
||||
|
||||
document.documentElement.style.setProperty(
|
||||
toCssVarName(styles.transformOrigin),
|
||||
transformOrigin
|
||||
);
|
||||
|
||||
document.documentElement.style.setProperty(
|
||||
toCssVarName(styles.animationTimeout),
|
||||
animationTimeout + 'ms'
|
||||
);
|
||||
}, [open, target]);
|
||||
return (
|
||||
<PeekViewContext.Provider value={emptyContext}>
|
||||
<Dialog.Root modal open={vtOpen} onOpenChange={onOpenChange}>
|
||||
<Dialog.Portal>
|
||||
<PeekViewModalOverlay
|
||||
ref={overlayRef}
|
||||
className={styles.modalOverlay}
|
||||
onAnimationStart={onAnimationStart}
|
||||
onAnimationEnd={onAnimateEnd}
|
||||
data-anime-state={animeState}
|
||||
/>
|
||||
<div
|
||||
ref={ref}
|
||||
data-testid={testId}
|
||||
data-padding={padding}
|
||||
data-peek-view-wrapper
|
||||
className={styles.modalContentWrapper}
|
||||
className={clsx(styles.modalContentWrapper)}
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
styles.modalContentContainer,
|
||||
padding && styles.containerPadding,
|
||||
animation === 'fade'
|
||||
? styles.modalContentContainerWithFade
|
||||
: styles.modalContentContainerWithZoom
|
||||
)}
|
||||
data-testid="peek-view-modal-animation-container"
|
||||
data-anime-state={animeState}
|
||||
ref={contentClipRef}
|
||||
className={styles.modalContentContainer}
|
||||
>
|
||||
<Dialog.Content
|
||||
{...contentOptions}
|
||||
className={styles.modalContent}
|
||||
className={clsx({
|
||||
[styles.modalContent]: true,
|
||||
[styles.dialog]: dialogFrame,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
<Dialog.Title style={{ display: 'none' }} />
|
||||
<div style={{ height: '100%' }} ref={contentRef}>
|
||||
{children}
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
{controls ? (
|
||||
<div className={styles.modalControls}>{controls}</div>
|
||||
<div ref={controlsRef} className={styles.modalControls}>
|
||||
{controls}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -78,7 +78,7 @@ export const DefaultPeekViewControls = ({
|
||||
icon: <CloseIcon />,
|
||||
nameKey: 'close',
|
||||
name: t['com.affine.peek-view-controls.close'](),
|
||||
onClick: peekView.close,
|
||||
onClick: () => peekView.close(),
|
||||
},
|
||||
].filter((opt): opt is ControlButtonProps => Boolean(opt));
|
||||
}, [peekView, t]);
|
||||
@ -109,7 +109,7 @@ export const DocPeekViewControls = ({
|
||||
icon: <CloseIcon />,
|
||||
nameKey: 'close',
|
||||
name: t['com.affine.peek-view-controls.close'](),
|
||||
onClick: peekView.close,
|
||||
onClick: () => peekView.close(),
|
||||
},
|
||||
{
|
||||
icon: <ExpandFullIcon />,
|
||||
@ -123,7 +123,7 @@ export const DocPeekViewControls = ({
|
||||
if (mode) {
|
||||
doc?.setMode(mode);
|
||||
}
|
||||
peekView.close();
|
||||
peekView.close('none');
|
||||
},
|
||||
},
|
||||
environment.isDesktop && {
|
||||
@ -132,7 +132,7 @@ export const DocPeekViewControls = ({
|
||||
name: t['com.affine.peek-view-controls.open-doc-in-split-view'](),
|
||||
onClick: () => {
|
||||
workbench.openDoc(docId, { at: 'beside' });
|
||||
peekView.close();
|
||||
peekView.close('none');
|
||||
},
|
||||
},
|
||||
!environment.isDesktop && {
|
||||
@ -144,7 +144,7 @@ export const DocPeekViewControls = ({
|
||||
`/workspace/${workspace.id}/${docId}#${blockId ?? ''}`,
|
||||
'_blank'
|
||||
);
|
||||
peekView.close();
|
||||
peekView.close('none');
|
||||
},
|
||||
},
|
||||
].filter((opt): opt is ControlButtonProps => Boolean(opt));
|
||||
|
@ -73,7 +73,7 @@ const getRendererProps = (
|
||||
? activePeekView.target
|
||||
: undefined,
|
||||
padding: activePeekView.info.type === 'doc',
|
||||
animation: activePeekView.info.type === 'image' ? 'fade' : 'zoom',
|
||||
dialogFrame: activePeekView.info.type !== 'image',
|
||||
};
|
||||
};
|
||||
|
||||
@ -104,7 +104,8 @@ export const PeekViewManagerModal = () => {
|
||||
return (
|
||||
<PeekViewModalContainer
|
||||
{...renderProps}
|
||||
open={show && !!renderProps}
|
||||
open={!!show?.value && !!renderProps}
|
||||
animation={show?.animation || 'none'}
|
||||
onOpenChange={open => {
|
||||
if (!open) {
|
||||
peekViewEntity.close();
|
||||
|
Loading…
Reference in New Issue
Block a user