mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 12:55:00 +00:00
feat: split components (#1530)
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
/* deepscan-disable USELESS_ARROW_FUNC_BIND */
|
||||
import { __unstableSchemas, builtInSchemas } from '@blocksuite/blocks/models';
|
||||
import { EditorContainer } from '@blocksuite/editor';
|
||||
import { Page, Workspace } from '@blocksuite/store';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryFn } from '@storybook/react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { BlockSuiteEditor, EditorProps } from '.';
|
||||
|
||||
function initPage(page: Page, editor: Readonly<EditorContainer>): void {
|
||||
// Add page block and surface block at root level
|
||||
const pageBlockId = page.addBlockByFlavour('affine:page', {
|
||||
title: new page.Text(''),
|
||||
});
|
||||
page.addBlockByFlavour('affine:surface', {}, null);
|
||||
const frameId = page.addBlockByFlavour('affine:frame', {}, pageBlockId);
|
||||
page.addBlockByFlavour('affine:paragraph', {}, frameId);
|
||||
page.resetHistory();
|
||||
}
|
||||
|
||||
const blockSuiteWorkspace = new Workspace({
|
||||
id: 'test',
|
||||
blobOptionsGetter: () => void 0,
|
||||
});
|
||||
blockSuiteWorkspace.register(builtInSchemas).register(__unstableSchemas);
|
||||
const promise = new Promise<void>(resolve => {
|
||||
blockSuiteWorkspace.slots.pageAdded.once(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
blockSuiteWorkspace.createPage('page0');
|
||||
|
||||
type BlockSuiteMeta = Meta<typeof BlockSuiteEditor>;
|
||||
export default {
|
||||
title: 'BlockSuite/Editor',
|
||||
component: BlockSuiteEditor,
|
||||
} satisfies BlockSuiteMeta;
|
||||
|
||||
const Template: StoryFn<EditorProps> = (args: EditorProps) => {
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const page = blockSuiteWorkspace.getPage('page0');
|
||||
useEffect(() => {
|
||||
promise
|
||||
.then(() => setLoaded(true))
|
||||
.then(() => {
|
||||
document.dispatchEvent(new Event('blocksuite:ready'));
|
||||
});
|
||||
}, []);
|
||||
if (!loaded || !page) return <div>Loading...</div>;
|
||||
return (
|
||||
<BlockSuiteEditor
|
||||
{...args}
|
||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||
page={page}
|
||||
/>
|
||||
);
|
||||
};
|
||||
export const Empty = Template.bind({});
|
||||
Empty.play = async ({ canvasElement }) => {
|
||||
await new Promise<void>(resolve => {
|
||||
document.addEventListener('blocksuite:ready', () => resolve(), {
|
||||
once: true,
|
||||
});
|
||||
});
|
||||
|
||||
const editorContainer = canvasElement.querySelector(
|
||||
'[data-testid="editor-test-page0"]'
|
||||
) as HTMLDivElement;
|
||||
expect(editorContainer).not.toBeNull();
|
||||
await new Promise<void>(resolve => {
|
||||
setTimeout(() => resolve(), 50);
|
||||
});
|
||||
const editor = editorContainer.querySelector(
|
||||
'editor-container'
|
||||
) as EditorContainer;
|
||||
expect(editor).not.toBeNull();
|
||||
};
|
||||
|
||||
Empty.args = {
|
||||
onInit: initPage,
|
||||
mode: 'page',
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
import { BlockHub } from '@blocksuite/blocks';
|
||||
import { EditorContainer } from '@blocksuite/editor';
|
||||
import type { Page, Workspace } from '@blocksuite/store';
|
||||
import { CSSProperties, useEffect, useRef } from 'react';
|
||||
|
||||
export type EditorProps = {
|
||||
blockSuiteWorkspace: Workspace;
|
||||
page: Page;
|
||||
mode: 'page' | 'edgeless';
|
||||
onInit: (page: Page, editor: Readonly<EditorContainer>) => void;
|
||||
onLoad?: (page: Page, editor: EditorContainer) => void;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var currentBlockSuiteWorkspace: Workspace | undefined;
|
||||
// eslint-disable-next-line no-var
|
||||
var currentPage: Page | undefined;
|
||||
// eslint-disable-next-line no-var
|
||||
var currentEditor: EditorContainer | undefined;
|
||||
}
|
||||
|
||||
export const BlockSuiteEditor = (props: EditorProps) => {
|
||||
const page = props.page;
|
||||
const editorRef = useRef<EditorContainer | null>(null);
|
||||
const blockHubRef = useRef<BlockHub | null>(null);
|
||||
if (editorRef.current === null) {
|
||||
editorRef.current = new EditorContainer();
|
||||
globalThis.currentEditor = editorRef.current;
|
||||
}
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
if (editorRef.current) {
|
||||
editorRef.current.mode = props.mode;
|
||||
}
|
||||
}, [props.mode]);
|
||||
|
||||
useEffect(() => {
|
||||
const editor = editorRef.current;
|
||||
if (!editor || !ref.current || !page) {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.page = page;
|
||||
if (page.root === null) {
|
||||
props.onInit(page, editor);
|
||||
}
|
||||
props.onLoad?.(page, editor);
|
||||
return;
|
||||
}, [page, props]);
|
||||
|
||||
useEffect(() => {
|
||||
const editor = editorRef.current;
|
||||
const container = ref.current;
|
||||
|
||||
if (!editor || !container || !page) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
page.awarenessStore.getFlag('enable_block_hub') &&
|
||||
props.mode === 'page'
|
||||
) {
|
||||
editor.createBlockHub().then(blockHub => {
|
||||
if (blockHubRef.current) {
|
||||
blockHubRef.current.remove();
|
||||
}
|
||||
blockHubRef.current = blockHub;
|
||||
const toolWrapper = document.querySelector('#toolWrapper');
|
||||
if (!toolWrapper) {
|
||||
console.warn(
|
||||
'toolWrapper not found, block hub feature will not be available.'
|
||||
);
|
||||
} else {
|
||||
toolWrapper.appendChild(blockHub);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
container.appendChild(editor);
|
||||
return () => {
|
||||
blockHubRef.current?.remove();
|
||||
container.removeChild(editor);
|
||||
};
|
||||
}, [page, props.mode]);
|
||||
return (
|
||||
<div
|
||||
data-testid={`editor-${props.blockSuiteWorkspace.id}-${props.page.id}`}
|
||||
className="editor-wrapper"
|
||||
style={props.style}
|
||||
ref={ref}
|
||||
/>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user