refactor: image preview component issues in center peek (#7313)

fix AF-948

- remove jotai
- every editor got its own image block modal
- image block uses center peek modal for animation and style
This commit is contained in:
pengx17
2024-06-25 07:34:18 +00:00
parent 76f6d91cdd
commit f51da066a8
9 changed files with 440 additions and 459 deletions
@@ -1,3 +1,4 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';
export const editor = style({
@@ -7,3 +8,12 @@ export const editor = style({
},
minHeight: '100%',
});
export const affineDocViewport = style({
display: 'flex',
flexDirection: 'column',
userSelect: 'none',
containerName: 'viewport',
containerType: 'inline-size',
background: cssVar('backgroundPrimaryColor'),
});
@@ -3,6 +3,7 @@ import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
import { AIProvider } from '@affine/core/blocksuite/presets/ai';
import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error-boundary';
import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor';
import { ImagePreviewModal } from '@affine/core/components/image-preview';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { PageNotFound } from '@affine/core/pages/404';
import { Bound, type EdgelessRootService } from '@blocksuite/blocks';
@@ -10,6 +11,7 @@ import { DisposableGroup } from '@blocksuite/global/utils';
import type { AffineEditorContainer } from '@blocksuite/presets';
import type { DocMode } from '@toeverything/infra';
import { DocsService, FrameworkScope, useService } from '@toeverything/infra';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { WorkbenchService } from '../../workbench';
@@ -145,7 +147,9 @@ export function DocPeekPreview({
return (
<AffineErrorBoundary>
<Scrollable.Root>
<Scrollable.Viewport className="affine-page-viewport">
<Scrollable.Viewport
className={clsx('affine-page-viewport', styles.affineDocViewport)}
>
<FrameworkScope scope={doc.scope}>
<BlockSuiteEditor
ref={onRef}
@@ -154,6 +158,13 @@ export function DocPeekPreview({
defaultSelectedBlockId={blockId}
page={doc.blockSuiteDoc}
/>
{editor?.host ? (
<ImagePreviewModal
pageId={doc.id}
docCollection={doc.blockSuiteDoc.collection}
host={editor.host}
/>
) : null}
</FrameworkScope>
</Scrollable.Viewport>
<Scrollable.Scrollbar />
@@ -3,8 +3,9 @@ import { createVar, keyframes, style } from '@vanilla-extract/css';
export const animationTimeout = createVar();
export const transformOrigin = createVar();
export const animationType = createVar();
const contentShow = keyframes({
const zoomIn = keyframes({
from: {
transform: 'scale(0.10)',
},
@@ -12,7 +13,7 @@ const contentShow = keyframes({
transform: 'scale(1)',
},
});
const contentHide = keyframes({
const zoomOut = keyframes({
to: {
opacity: 0,
transform: 'scale(0.10)',
@@ -93,32 +94,57 @@ export const modalContentWrapper = style({
export const modalContentContainer = style({
display: 'flex',
alignItems: 'flex-start',
width: '90%',
height: '90%',
maxWidth: 1248,
width: '100%',
height: '100%',
willChange: 'transform, opacity',
transformOrigin: transformOrigin,
selectors: {
'&[data-state=entered], &[data-state=entering]': {
animationFillMode: 'forwards',
animationName: contentShow,
animationDuration: animationTimeout,
animationTimingFunction: 'cubic-bezier(0.42, 0, 0.58, 1)',
},
'&[data-state=exited], &[data-state=exiting]': {
animationFillMode: 'forwards',
animationName: contentHide,
animationDuration: animationTimeout,
animationTimingFunction: 'cubic-bezier(0.42, 0, 0.58, 1)',
},
},
});
export const modalContentContainerWithZoom = style({
selectors: {
'&[data-state=entered], &[data-state=entering]': {
animationName: zoomIn,
},
'&[data-state=exited], &[data-state=exiting]': {
animationName: zoomOut,
},
},
});
export const modalContentContainerWithFade = style({
selectors: {
'&[data-state=entered], &[data-state=entering]': {
animationName: fadeIn,
},
'&[data-state=exited], &[data-state=exiting]': {
animationName: fadeOut,
},
},
});
export const containerPadding = style({
width: '90%',
height: '90%',
maxWidth: 1248,
});
export const modalContent = style({
flex: 1,
height: '100%',
backgroundColor: cssVar('backgroundOverlayPanelColor'),
boxShadow: '0px 0px 0px 2.23px rgba(0, 0, 0, 0.08)',
backdropFilter: 'drop-shadow(0px 0px 2px rgba(0, 0, 0, 0.08))',
borderRadius: '8px',
minHeight: 300,
// :focus-visible will set outline
@@ -1,6 +1,6 @@
import * as Dialog from '@radix-ui/react-dialog';
import { cssVar } from '@toeverything/theme';
import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import {
createContext,
type PropsWithChildren,
@@ -24,7 +24,7 @@ const contentOptions: Dialog.DialogContentProps = {
},
style: {
padding: 0,
backgroundColor: cssVar('backgroundPrimaryColor'),
backgroundColor: 'transparent',
overflow: 'hidden',
},
};
@@ -64,14 +64,22 @@ export const PeekViewModalContainer = ({
controls,
children,
hideOnEntering,
onAnimationStart,
onAnimateEnd,
animation = 'zoom',
padding = true,
testId,
}: PropsWithChildren<{
open: boolean;
hideOnEntering?: boolean;
target?: HTMLElement;
onOpenChange: (open: boolean) => void;
controls: React.ReactNode;
controls?: React.ReactNode;
onAnimationStart?: () => void;
onAnimateEnd?: () => void;
padding?: boolean;
animation?: 'fade' | 'zoom';
testId?: string;
}>) => {
const [{ status }, toggle] = useTransition({
timeout: animationTimeout,
@@ -100,11 +108,11 @@ export const PeekViewModalContainer = ({
[styles.transformOrigin]: transformOrigin,
[styles.animationTimeout]: `${animationTimeout}ms`,
})}
onAnimationEnd={() => {
onAnimateEnd?.();
}}
onAnimationStart={onAnimationStart}
onAnimationEnd={onAnimateEnd}
/>
<div
data-testid={testId}
data-peek-view-wrapper
className={styles.modalContentWrapper}
style={assignInlineVars({
@@ -113,7 +121,13 @@ export const PeekViewModalContainer = ({
})}
>
<div
className={styles.modalContentContainer}
className={clsx(
styles.modalContentContainer,
padding && styles.containerPadding,
animation === 'fade'
? styles.modalContentContainerWithFade
: styles.modalContentContainerWithZoom
)}
data-testid="peek-view-modal-animation-container"
data-state={status}
>
@@ -123,9 +137,11 @@ export const PeekViewModalContainer = ({
>
{hideOnEntering && status === 'entering' ? null : children}
</Dialog.Content>
<div data-state={status} className={styles.modalControls}>
{controls}
</div>
{controls ? (
<div data-state={status} className={styles.modalControls}>
{controls}
</div>
) : null}
</div>
</div>
</Dialog.Portal>