diff --git a/packages/frontend/core/src/components/affine/page-history-modal/data.ts b/packages/frontend/core/src/components/affine/page-history-modal/data.ts index 6eb0f5f9a0..3debe46c18 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/data.ts +++ b/packages/frontend/core/src/components/affine/page-history-modal/data.ts @@ -63,11 +63,7 @@ export const useDocSnapshotList = (workspaceId: string, pageDocId: string) => { return data.flatMap(page => page.workspace.histories); }, [data]); - return [ - histories, - shouldLoadMore ? loadMore : undefined, - loadingMore, - ] as const; + return [histories, shouldLoadMore ? loadMore : false, !!loadingMore] as const; }; const snapshotFetcher = async ( diff --git a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx index 9efed36753..7210c21258 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx +++ b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx @@ -17,6 +17,7 @@ import type { DialogContentProps } from '@radix-ui/react-dialog'; import { Doc, type PageMode, Workspace } from '@toeverything/infra'; import { useService } from '@toeverything/infra/di'; import { atom, useAtom, useSetAtom } from 'jotai'; +import { range } from 'lodash-es'; import { Fragment, type PropsWithChildren, @@ -24,6 +25,7 @@ import { useCallback, useLayoutEffect, useMemo, + useRef, useState, } from 'react'; import { encodeStateAsUpdate } from 'yjs'; @@ -90,6 +92,7 @@ const ModalContainer = ({ interface HistoryEditorPreviewProps { ts?: string; + historyList: HistoryList; snapshotPage?: BlockSuiteDoc; mode: PageMode; onModeChange: (mode: PageMode) => void; @@ -98,6 +101,7 @@ interface HistoryEditorPreviewProps { const HistoryEditorPreview = ({ ts, + historyList, snapshotPage, onModeChange, mode, @@ -110,11 +114,9 @@ const HistoryEditorPreview = ({ onModeChange('edgeless'); }, [onModeChange]); - return ( -
-
-
-
+ const content = useMemo(() => { + return ( + <>
)} -
+ + ); + }, [ + mode, + onSwitchToEdgelessMode, + onSwitchToPageMode, + snapshotPage, + title, + ts, + ]); + + const previewRef = useRef(null); + + return ( +
+ {range(0, historyList.length).map(i => { + const historyIndex = historyList.findIndex(h => h.timestamp === ts); + const distance = i - historyIndex; + const flag = + distance === 0 + ? 'current' + : distance > 2 + ? '> 2' + : distance < 0 + ? '< 0' + : distance; + return ( +
+ {historyIndex === i ? content : null} +
+ ); + })}
); }; @@ -240,21 +273,21 @@ const PlanPrompt = () => { ) : null; }; +type HistoryList = ReturnType[0]; + const PageHistoryList = ({ - pageDocId, - workspaceId, + historyList, + onLoadMore, + loadingMore, activeVersion, onVersionChange, }: { - workspaceId: string; - pageDocId: string; activeVersion?: string; onVersionChange: (version: string) => void; + historyList: HistoryList; + onLoadMore: (() => void) | false; + loadingMore: boolean; }) => { - const [historyList, loadMore, loadingMore] = useDocSnapshotList( - workspaceId, - pageDocId - ); const historyListByDay = useMemo(() => { return historyListGroupByDay(historyList); }, [historyList]); @@ -330,13 +363,13 @@ const PageHistoryList = ({ ); })} - {loadMore ? ( + {onLoadMore ? ( @@ -468,11 +501,17 @@ const PageHistoryManager = ({ [handleRestore] ); + const [historyList, loadMore, loadingMore] = useDocSnapshotList( + workspaceId, + pageDocId + ); + return (
diff --git a/packages/frontend/core/src/components/affine/page-history-modal/styles.css.ts b/packages/frontend/core/src/components/affine/page-history-modal/styles.css.ts index 3bf4770ec3..5d26b6606d 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/styles.css.ts +++ b/packages/frontend/core/src/components/affine/page-history-modal/styles.css.ts @@ -30,6 +30,7 @@ export const previewWrapper = style({ flexGrow: 1, height: '100%', position: 'relative', + zIndex: 0, overflow: 'hidden', width: `calc(100% - ${historyListWidth})`, backgroundColor: cssVar('backgroundSecondaryColor'), @@ -47,24 +48,38 @@ export const previewContainer = style({ boxShadow: cssVar('shadow3'), height: 'calc(100% - 40px)', width: `calc(100% - 80px)`, - backgroundColor: cssVar('backgroundSecondaryColor'), + backgroundColor: cssVar('backgroundPrimaryColor'), + transformOrigin: 'top center', + transition: 'all 0.5s ease-in-out', + selectors: { + '&[data-distance="> 2"]': { + transform: 'scale(0.60)', + opacity: 0, + zIndex: -3, + pointerEvents: 'none', + }, + '&[data-distance="2"]': { + transform: 'scale(0.90) translateY(-16px)', + zIndex: -2, + pointerEvents: 'none', + }, + '&[data-distance="1"]': { + transform: 'scale(0.95) translateY(-8px)', + zIndex: -1, + pointerEvents: 'none', + }, + '&[data-distance="current"]': { + opacity: 1, + zIndex: 0, + }, + '&[data-distance="< 0"]': { + transform: 'scale(1.60) translateY(18px)', + opacity: 0, + zIndex: 1, + pointerEvents: 'none', + }, + }, }); -export const previewContainerStack1 = style([ - previewContainer, - { - left: 48, - height: 'calc(100% - 32px)', - width: `calc(100% - 96px)`, - }, -]); -export const previewContainerStack2 = style([ - previewContainer, - { - left: 56, - height: 'calc(100% - 24px)', - width: `calc(100% - 112px)`, - }, -]); export const previewHeader = style({ display: 'flex', alignItems: 'center', diff --git a/tests/storybook/src/stories/page-list.stories.tsx b/tests/storybook/src/stories/page-list.stories.tsx index 429ce5e5f8..52c40087b6 100644 --- a/tests/storybook/src/stories/page-list.stories.tsx +++ b/tests/storybook/src/stories/page-list.stories.tsx @@ -13,14 +13,20 @@ import { PageTags, type PageTagsProps, } from '@affine/core/components/page-list'; +import { workbenchRoutes } from '@affine/core/router'; import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import { PageIcon, TagsIcon } from '@blocksuite/icons'; import { Schema, Workspace } from '@blocksuite/store'; +import { expect } from '@storybook/jest'; import type { Meta, StoryFn } from '@storybook/react'; import { userEvent } from '@storybook/testing-library'; import { initEmptyPage } from '@toeverything/infra'; import { useState } from 'react'; -import { withRouter } from 'storybook-addon-react-router-v6'; +import { + reactRouterOutlets, + reactRouterParameters, + withRouter, +} from 'storybook-addon-react-router-v6'; export default { title: 'AFFiNE/PageList', @@ -41,6 +47,11 @@ AffineOperationCell.args = { onDisablePublicSharing: () => toast('Disable public sharing'), onRemoveToTrash: () => toast('Remove to trash'), }; +AffineOperationCell.parameters = { + reactRouter: reactRouterParameters({ + routing: reactRouterOutlets(workbenchRoutes), + }), +}; AffineOperationCell.play = async ({ canvasElement }) => { { const button = canvasElement.querySelector(