From 1716e7a3978c353e80fd110b56cef82a9bf89b93 Mon Sep 17 00:00:00 2001 From: Himself65 Date: Sun, 4 Jun 2023 23:33:36 +0800 Subject: [PATCH] fix: use `react-resizable-panels` (#2679) --- apps/web/package.json | 2 +- .../web/src/components/page-detail-editor.tsx | 124 +++++++++--------- apps/web/src/pages/_app.tsx | 1 - packages/plugin-infra/src/type.ts | 16 ++- plugins/copilot/src/UI/header-item.tsx | 2 +- yarn.lock | 108 ++------------- 6 files changed, 88 insertions(+), 165 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 277fd6d177..bbbce1a82b 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -47,7 +47,7 @@ "react": "18.3.0-canary-16d053d59-20230506", "react-dom": "18.3.0-canary-16d053d59-20230506", "react-is": "^18.2.0", - "react-mosaic-component": "^6.0.1", + "react-resizable-panels": "^0.0.48", "rxjs": "^7.8.1", "swr": "^2.1.5", "y-protocols": "^1.0.5", diff --git a/apps/web/src/components/page-detail-editor.tsx b/apps/web/src/components/page-detail-editor.tsx index 74d802e6bd..f5e8427a71 100644 --- a/apps/web/src/components/page-detail-editor.tsx +++ b/apps/web/src/components/page-detail-editor.tsx @@ -1,6 +1,6 @@ import './page-detail-editor.css'; -import { PageNotFoundError, Unreachable } from '@affine/env/constant'; +import { PageNotFoundError } from '@affine/env/constant'; import { rootCurrentEditorAtom } from '@affine/workspace/atom'; import type { EditorContainer } from '@blocksuite/editor'; import type { Page } from '@blocksuite/store'; @@ -9,33 +9,29 @@ import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page- import { useBlockSuiteWorkspacePage } from '@toeverything/hooks/use-block-suite-workspace-page'; import { useBlockSuiteWorkspacePageTitle } from '@toeverything/hooks/use-block-suite-workspace-page-title'; import { affinePluginsAtom } from '@toeverything/plugin-infra/manager'; -import type { PluginUIAdapter } from '@toeverything/plugin-infra/type'; -import type { ExpectedLayout } from '@toeverything/plugin-infra/type'; +import type { + AffinePlugin, + LayoutNode, + PluginUIAdapter, +} from '@toeverything/plugin-infra/type'; import type { PluginBlockSuiteAdapter } from '@toeverything/plugin-infra/type'; -import { useAtom, useAtomValue, useSetAtom } from 'jotai'; +import { useAtomValue, useSetAtom } from 'jotai'; import Head from 'next/head'; -import type { FC } from 'react'; +import type { FC, ReactElement } from 'react'; import React, { - lazy, memo, startTransition, Suspense, useCallback, useMemo, } from 'react'; -import type { MosaicNode } from 'react-mosaic-component'; +import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { workspacePreferredModeAtom } from '../atoms'; import { contentLayoutAtom } from '../atoms/layout'; import type { AffineOfficialWorkspace } from '../shared'; import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor'; -const Mosaic = lazy(() => - import('react-mosaic-component').then(({ Mosaic }) => ({ - default: Mosaic, - })) -); - export type PageDetailEditorProps = { isPublic?: boolean; workspace: AffineOfficialWorkspace; @@ -127,6 +123,54 @@ const PluginContentAdapter = memo<{ ); }); +type LayoutPanelProps = { + node: LayoutNode; + editorProps: PageDetailEditorProps; + plugins: AffinePlugin[]; +}; + +const LayoutPanel = memo(function LayoutPanel( + props: LayoutPanelProps +): ReactElement { + const node = props.node; + if (typeof node === 'string') { + if (node === 'editor') { + return ; + } else { + const plugin = props.plugins.find( + plugin => plugin.definition.id === node + ); + const Content = plugin?.uiAdapter.detailContent; + assertExists(Content); + return ; + } + } else { + return ( + + + + + + + + + + + + + + ); + } +}); + export const PageDetailEditor: FC = props => { const { workspace, pageId } = props; const blockSuiteWorkspace = workspace.blockSuiteWorkspace; @@ -135,66 +179,22 @@ export const PageDetailEditor: FC = props => { throw new PageNotFoundError(blockSuiteWorkspace, pageId); } const title = useBlockSuiteWorkspacePageTitle(blockSuiteWorkspace, pageId); + + const layout = useAtomValue(contentLayoutAtom); const affinePluginsMap = useAtomValue(affinePluginsAtom); const plugins = useMemo( () => Object.values(affinePluginsMap), [affinePluginsMap] ); - const [layout, setLayout] = useAtom(contentLayoutAtom); - - const onChange = useCallback( - (_: MosaicNode | null) => { - // type cast - const node = _ as MosaicNode | null; - if (node) { - if (typeof node === 'string') { - console.error('unexpected layout'); - } else { - if (node.splitPercentage && node.splitPercentage < 70) { - return; - } else if (node.first !== 'editor') { - return; - } - setLayout(node as ExpectedLayout); - } - } - }, - [setLayout] - ); - return ( <> {title} - {layout === 'editor' ? ( - - ) : ( - { - if (id === 'editor') { - return ; - } else { - const plugin = plugins.find( - plugin => plugin.definition.id === id - ); - if (plugin && plugin.uiAdapter.detailContent) { - return ( - - - - ); - } - } - throw new Unreachable(); - }} - value={layout} - /> - )} + + + ); }; diff --git a/apps/web/src/pages/_app.tsx b/apps/web/src/pages/_app.tsx index 5c8dad37e0..10cc84ca22 100644 --- a/apps/web/src/pages/_app.tsx +++ b/apps/web/src/pages/_app.tsx @@ -1,6 +1,5 @@ import '@affine/component/theme/global.css'; import '@affine/component/theme/theme.css'; -import 'react-mosaic-component/react-mosaic-component.css'; // bootstrap code before everything import '../bootstrap'; diff --git a/packages/plugin-infra/src/type.ts b/packages/plugin-infra/src/type.ts index 58ac07c520..20a0524503 100644 --- a/packages/plugin-infra/src/type.ts +++ b/packages/plugin-infra/src/type.ts @@ -10,7 +10,6 @@ import type { Workspace } from '@blocksuite/store'; import type { Page } from '@playwright/test'; import type { WritableAtom } from 'jotai'; import type { ReactElement } from 'react'; -import type { MosaicDirection, MosaicNode } from 'react-mosaic-component'; /** * A code loader interface of the plugin API. @@ -81,14 +80,23 @@ export enum ReleaseStage { DEV = 'dev', } +export type LayoutDirection = 'horizontal' | 'vertical'; +export type LayoutNode = LayoutParentNode | string; +export type LayoutParentNode = { + direction: LayoutDirection; + splitPercentage: number; // 0 - 100 + first: LayoutNode; + second: LayoutNode; +}; + export type ExpectedLayout = | { - direction: MosaicDirection; + direction: LayoutDirection; // the first element is always the editor first: 'editor'; - second: MosaicNode; + second: LayoutNode; // the percentage should be greater than 70 - splitPercentage?: number; + splitPercentage: number; } | 'editor'; diff --git a/plugins/copilot/src/UI/header-item.tsx b/plugins/copilot/src/UI/header-item.tsx index 984225aa3c..054573707c 100644 --- a/plugins/copilot/src/UI/header-item.tsx +++ b/plugins/copilot/src/UI/header-item.tsx @@ -16,7 +16,7 @@ export const HeaderItem: PluginUIAdapter['headerItem'] = ({ setLayout(layout => { if (layout === 'editor') { return { - direction: 'row', + direction: 'horizontal', first: 'editor', second: 'com.affine.copilot', splitPercentage: 80, diff --git a/yarn.lock b/yarn.lock index d54c97372b..13d649cb45 100644 --- a/yarn.lock +++ b/yarn.lock @@ -395,7 +395,7 @@ __metadata: react: 18.3.0-canary-16d053d59-20230506 react-dom: 18.3.0-canary-16d053d59-20230506 react-is: ^18.2.0 - react-mosaic-component: ^6.0.1 + react-resizable-panels: ^0.0.48 redux: ^4.2.1 rxjs: ^7.8.1 swc-plugin-coverage-instrument: ^0.0.18 @@ -12483,13 +12483,6 @@ __metadata: languageName: node linkType: hard -"classnames@npm:^2.3.2": - version: 2.3.2 - resolution: "classnames@npm:2.3.2" - checksum: 2c62199789618d95545c872787137262e741f9db13328e216b093eea91c85ef2bfb152c1f9e63027204e2559a006a92eb74147d46c800a9f96297ae1d9f96f4e - languageName: node - linkType: hard - "clean-regexp@npm:^1.0.0": version: 1.0.0 resolution: "clean-regexp@npm:1.0.0" @@ -14013,13 +14006,6 @@ __metadata: languageName: node linkType: hard -"dnd-multi-backend@npm:^8.0.0": - version: 8.0.0 - resolution: "dnd-multi-backend@npm:8.0.0" - checksum: ce19b14c509e3740f6ab8cab725f8df2e7ad79fd04cb7d57d4b3f9e579cd5ab1f9439517b4325e014ca1caeab6152adced993085b734aff5a5d40cbb766a5da0 - languageName: node - linkType: hard - "doctrine@npm:^2.1.0": version: 2.1.0 resolution: "doctrine@npm:2.1.0" @@ -17545,13 +17531,6 @@ __metadata: languageName: node linkType: hard -"immutability-helper@npm:^3.1.1": - version: 3.1.1 - resolution: "immutability-helper@npm:3.1.1" - checksum: 6fdbf6d2123efa567263e904bbaff07aca0e24560d270d34967b03aab8ec20bd3e4057f394d59e50eb6c4718c9415591a6281692bb0aafd522ad72cf4887133f - languageName: node - linkType: hard - "immutable@npm:^4.2.2": version: 4.3.0 resolution: "immutable@npm:4.3.0" @@ -23367,17 +23346,6 @@ __metadata: languageName: node linkType: hard -"rdndmb-html5-to-touch@npm:^8.0.0": - version: 8.0.0 - resolution: "rdndmb-html5-to-touch@npm:8.0.0" - dependencies: - dnd-multi-backend: ^8.0.0 - react-dnd-html5-backend: ^16.0.1 - react-dnd-touch-backend: ^16.0.1 - checksum: 9ede5db33820ca592f0c0aaed3b9a6cd9026cd7ffe60c2cdabd62275ab253b3a69db2b4eb909c250602b98d413bf635380ba6d5df574ca002d076fdc12f94606 - languageName: node - linkType: hard - "react-base16-styling@npm:^0.9.1": version: 0.9.1 resolution: "react-base16-styling@npm:0.9.1" @@ -23403,49 +23371,7 @@ __metadata: languageName: node linkType: hard -"react-dnd-html5-backend@npm:^16.0.1": - version: 16.0.1 - resolution: "react-dnd-html5-backend@npm:16.0.1" - dependencies: - dnd-core: ^16.0.1 - checksum: e2368bf85d5632a5cd867b743feb54c9052d909ea5331608860fa455edf3c633ac791f5b338e3db29b19ea8670c0ba5fb43c9c1c2510760bea030811d726cdfa - languageName: node - linkType: hard - -"react-dnd-multi-backend@npm:^8.0.0": - version: 8.0.0 - resolution: "react-dnd-multi-backend@npm:8.0.0" - dependencies: - dnd-multi-backend: ^8.0.0 - react-dnd-preview: ^8.0.0 - peerDependencies: - react: ^16.14.0 || ^17.0.2 || ^18.0.0 - react-dom: ^16.14.0 || ^17.0.2 || ^18.0.0 - checksum: a0871a4845df814f3a57b5c110e1601439d74d6433d2115a2078a4d49a200aa33a91b37a23d6dafaebc2ab857a25caa7d83d28cee034aa6907f7224c6257e56f - languageName: node - linkType: hard - -"react-dnd-preview@npm:^8.0.0": - version: 8.0.0 - resolution: "react-dnd-preview@npm:8.0.0" - peerDependencies: - react: ^16.14.0 || ^17.0.2 || ^18.0.0 - react-dnd: ^16.0.1 - checksum: abed644a2ccd454a8fc54fa5bc4904beeb8ccf40c1ea20fb3c5ff178733062fd9f5d657435fb671c5736c93d9e7b2ce8e7de8c07cbc9822b459f02fdbfc99b77 - languageName: node - linkType: hard - -"react-dnd-touch-backend@npm:^16.0.1": - version: 16.0.1 - resolution: "react-dnd-touch-backend@npm:16.0.1" - dependencies: - "@react-dnd/invariant": ^4.0.1 - dnd-core: ^16.0.1 - checksum: 5362c5f4266e3655d02c716f341dc55c61fae53e12eb3b239245bafb7a3f3cdb953e27e187e8dcb9d86c01bc738853d764d7c85b87104a49a8a8f1ecc4d35755 - languageName: node - linkType: hard - -"react-dnd@npm:*, react-dnd@npm:^16.0.1": +"react-dnd@npm:*": version: 16.0.1 resolution: "react-dnd@npm:16.0.1" dependencies: @@ -23616,26 +23542,6 @@ __metadata: languageName: node linkType: hard -"react-mosaic-component@npm:^6.0.1": - version: 6.0.1 - resolution: "react-mosaic-component@npm:6.0.1" - dependencies: - classnames: ^2.3.2 - immutability-helper: ^3.1.1 - lodash: ^4.17.21 - prop-types: ^15.8.1 - rdndmb-html5-to-touch: ^8.0.0 - react-dnd: ^16.0.1 - react-dnd-html5-backend: ^16.0.1 - react-dnd-multi-backend: ^8.0.0 - react-dnd-touch-backend: ^16.0.1 - uuid: ^9.0.0 - peerDependencies: - react: 16 - 18 - checksum: fb31b655fd841d09a5ca922f391c29cad8c7b0e87ac5d84386a434cb7d01f5a633f116b8c7ef4b79203cf0b1768a43c27ba65d41f22feba46a141be366b31de2 - languageName: node - linkType: hard - "react-refresh@npm:^0.14.0": version: 0.14.0 resolution: "react-refresh@npm:0.14.0" @@ -23707,6 +23613,16 @@ __metadata: languageName: node linkType: hard +"react-resizable-panels@npm:^0.0.48": + version: 0.0.48 + resolution: "react-resizable-panels@npm:0.0.48" + peerDependencies: + react: ^16.14.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 + checksum: dbc4ef9ac6a1b74a95bb81185b6bffbf8f1f1ab544090383e766e065979258bdafcf4485bd5507a5fd247eb02e6d060bcc05765c0e12c0ff47c4800a2cc3d7bb + languageName: node + linkType: hard + "react-style-singleton@npm:^2.2.1": version: 2.2.1 resolution: "react-style-singleton@npm:2.2.1"