mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-24 18:02:47 +08:00
feat(core): observe editor settings change and update edgeless editor (#8105)
<div class='graphite__hidden'>
<div>🎥 Video uploaded on Graphite:</div>
<a href="https://app.graphite.dev/media/video/sJGviKxfE3Ap685cl5bj/cfdd0578-274c-42e3-9ef5-5528e8b25306.mov">
<img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/sJGviKxfE3Ap685cl5bj/cfdd0578-274c-42e3-9ef5-5528e8b25306.mov">
</a>
</div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/sJGviKxfE3Ap685cl5bj/cfdd0578-274c-42e3-9ef5-5528e8b25306.mov">录屏2024-09-05 14.32.41.mov</video>
This commit is contained in:
@@ -191,6 +191,8 @@ export const ConnectorSettings = () => {
|
|||||||
<EdgelessSnapshot
|
<EdgelessSnapshot
|
||||||
title={t['com.affine.settings.editorSettings.edgeless.connecter']()}
|
title={t['com.affine.settings.editorSettings.edgeless.connecter']()}
|
||||||
docName="connector"
|
docName="connector"
|
||||||
|
keyName="connector"
|
||||||
|
flavour="connector"
|
||||||
/>
|
/>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
name={t[
|
name={t[
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,8 @@ export const MindMapSettings = () => {
|
|||||||
<EdgelessSnapshot
|
<EdgelessSnapshot
|
||||||
title={t['com.affine.settings.editorSettings.edgeless.mind-map']()}
|
title={t['com.affine.settings.editorSettings.edgeless.mind-map']()}
|
||||||
docName="mindmap"
|
docName="mindmap"
|
||||||
|
keyName={'mindmap' as any}
|
||||||
|
flavour="mindmap"
|
||||||
height={320}
|
height={320}
|
||||||
/>
|
/>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
|
|||||||
@@ -170,6 +170,8 @@ export const NoteSettings = () => {
|
|||||||
<EdgelessSnapshot
|
<EdgelessSnapshot
|
||||||
title={t['com.affine.settings.editorSettings.edgeless.note']()}
|
title={t['com.affine.settings.editorSettings.edgeless.note']()}
|
||||||
docName="note"
|
docName="note"
|
||||||
|
keyName="affine:note"
|
||||||
|
flavour="affine:note"
|
||||||
/>
|
/>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
name={t[
|
name={t[
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ export const PenSettings = () => {
|
|||||||
<EdgelessSnapshot
|
<EdgelessSnapshot
|
||||||
title={t['com.affine.settings.editorSettings.edgeless.pen']()}
|
title={t['com.affine.settings.editorSettings.edgeless.pen']()}
|
||||||
docName="pen"
|
docName="pen"
|
||||||
|
keyName="brush"
|
||||||
|
flavour="brush"
|
||||||
/>
|
/>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
name={t['com.affine.settings.editorSettings.edgeless.pen.color']()}
|
name={t['com.affine.settings.editorSettings.edgeless.pen.color']()}
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ export const ShapeSettings = () => {
|
|||||||
<EdgelessSnapshot
|
<EdgelessSnapshot
|
||||||
title={t['com.affine.settings.editorSettings.edgeless.shape']()}
|
title={t['com.affine.settings.editorSettings.edgeless.shape']()}
|
||||||
docName="shape"
|
docName="shape"
|
||||||
|
keyName="shape"
|
||||||
|
flavour="shape"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
|
import type { EditorSettingSchema } from '@affine/core/modules/editor-settting';
|
||||||
|
import { EditorSettingService } from '@affine/core/modules/editor-settting';
|
||||||
|
import type { EditorHost } from '@blocksuite/block-std';
|
||||||
import { BlockStdScope } from '@blocksuite/block-std';
|
import { BlockStdScope } from '@blocksuite/block-std';
|
||||||
|
import type { SurfaceBlockModel } from '@blocksuite/block-std/gfx';
|
||||||
import type { EdgelessRootService, FrameBlockModel } from '@blocksuite/blocks';
|
import type { EdgelessRootService, FrameBlockModel } from '@blocksuite/blocks';
|
||||||
import { SpecProvider } from '@blocksuite/blocks';
|
import { SpecProvider } from '@blocksuite/blocks';
|
||||||
import { Bound } from '@blocksuite/global/utils';
|
import { Bound } from '@blocksuite/global/utils';
|
||||||
import type { Doc } from '@blocksuite/store';
|
import type { Doc } from '@blocksuite/store';
|
||||||
|
import { useFramework } from '@toeverything/infra';
|
||||||
|
import { isEqual } from 'lodash-es';
|
||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
import { map, pairwise } from 'rxjs';
|
||||||
|
|
||||||
import { snapshotContainer, snapshotTitle } from '../style.css';
|
import { snapshotContainer, snapshotTitle } from '../style.css';
|
||||||
import { type DocName, getDocByName } from './docs';
|
import { type DocName, getDocByName } from './docs';
|
||||||
@@ -11,9 +18,16 @@ import { type DocName, getDocByName } from './docs';
|
|||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
docName: DocName;
|
docName: DocName;
|
||||||
|
flavour: BlockSuite.EdgelessModelKeys;
|
||||||
|
keyName: keyof EditorSettingSchema;
|
||||||
height?: number;
|
height?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSurfaceBlock(doc: Doc) {
|
||||||
|
const blocks = doc.getBlocksByFlavour('affine:surface');
|
||||||
|
return blocks.length !== 0 ? (blocks[0].model as SurfaceBlockModel) : null;
|
||||||
|
}
|
||||||
|
|
||||||
function getFrameBlock(doc: Doc) {
|
function getFrameBlock(doc: Doc) {
|
||||||
const blocks = doc.getBlocksByFlavour('affine:frame');
|
const blocks = doc.getBlocksByFlavour('affine:frame');
|
||||||
return blocks.length !== 0 ? (blocks[0].model as FrameBlockModel) : null;
|
return blocks.length !== 0 ? (blocks[0].model as FrameBlockModel) : null;
|
||||||
@@ -22,8 +36,30 @@ function getFrameBlock(doc: Doc) {
|
|||||||
const boundMap = new Map<DocName, Bound>();
|
const boundMap = new Map<DocName, Bound>();
|
||||||
|
|
||||||
export const EdgelessSnapshot = (props: Props) => {
|
export const EdgelessSnapshot = (props: Props) => {
|
||||||
const { title, docName, height = 180 } = props;
|
const { title, docName, flavour, keyName, height = 180 } = props;
|
||||||
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const docRef = useRef<Doc | null>(null);
|
||||||
|
const editorHostRef = useRef<EditorHost | null>(null);
|
||||||
|
const framework = useFramework();
|
||||||
|
const { editorSetting } = framework.get(EditorSettingService);
|
||||||
|
|
||||||
|
const updateElement = useCallback(
|
||||||
|
(props: Record<string, unknown>) => {
|
||||||
|
const editorHost = editorHostRef.current;
|
||||||
|
const doc = docRef.current;
|
||||||
|
if (!editorHost || !doc) return;
|
||||||
|
const edgelessService = editorHost.std.getService(
|
||||||
|
'affine:page'
|
||||||
|
) as EdgelessRootService;
|
||||||
|
const blocks = doc.getBlocksByFlavour(flavour);
|
||||||
|
const surface = getSurfaceBlock(doc);
|
||||||
|
const elements = surface?.getElementsByType(flavour) || [];
|
||||||
|
[...blocks, ...elements].forEach(ele => {
|
||||||
|
edgelessService.updateElement(ele.id, props);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[flavour]
|
||||||
|
);
|
||||||
|
|
||||||
const renderEditor = useCallback(async () => {
|
const renderEditor = useCallback(async () => {
|
||||||
if (!wrapperRef.current) return;
|
if (!wrapperRef.current) return;
|
||||||
@@ -34,12 +70,15 @@ export const EdgelessSnapshot = (props: Props) => {
|
|||||||
doc,
|
doc,
|
||||||
extensions: SpecProvider.getInstance().getSpec('edgeless:preview').value,
|
extensions: SpecProvider.getInstance().getSpec('edgeless:preview').value,
|
||||||
}).render();
|
}).render();
|
||||||
|
docRef.current = doc;
|
||||||
|
editorHostRef.current = editorHost;
|
||||||
|
const settings = editorSetting.get(keyName);
|
||||||
|
updateElement(settings as any);
|
||||||
|
|
||||||
|
// refresh viewport
|
||||||
const edgelessService = editorHost.std.getService(
|
const edgelessService = editorHost.std.getService(
|
||||||
'affine:page'
|
'affine:page'
|
||||||
) as EdgelessRootService;
|
) as EdgelessRootService;
|
||||||
|
|
||||||
// refresh viewport
|
|
||||||
edgelessService.specSlots.viewConnected.once(({ component }) => {
|
edgelessService.specSlots.viewConnected.once(({ component }) => {
|
||||||
const edgelessBlock = component as any;
|
const edgelessBlock = component as any;
|
||||||
edgelessBlock.editorViewportSelector = 'ref-viewport';
|
edgelessBlock.editorViewportSelector = 'ref-viewport';
|
||||||
@@ -54,14 +93,36 @@ export const EdgelessSnapshot = (props: Props) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// append to dom node
|
||||||
wrapperRef.current.append(editorHost);
|
wrapperRef.current.append(editorHost);
|
||||||
}, [docName]);
|
}, [docName, editorSetting, keyName, updateElement]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
renderEditor();
|
renderEditor();
|
||||||
}, [renderEditor]);
|
}, [renderEditor]);
|
||||||
|
|
||||||
|
// observe editor settings change
|
||||||
|
useEffect(() => {
|
||||||
|
const sub = editorSetting.provider
|
||||||
|
.watchAll()
|
||||||
|
.pipe(
|
||||||
|
map(settings => {
|
||||||
|
if (typeof settings[keyName] === 'string') {
|
||||||
|
return JSON.parse(settings[keyName]);
|
||||||
|
}
|
||||||
|
return keyName;
|
||||||
|
}),
|
||||||
|
pairwise()
|
||||||
|
)
|
||||||
|
.subscribe(([prev, current]) => {
|
||||||
|
if (!isEqual(prev, current)) {
|
||||||
|
updateElement(current);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => sub.unsubscribe();
|
||||||
|
}, [editorSetting.provider, flavour, keyName, updateElement]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={snapshotContainer}>
|
<div className={snapshotContainer}>
|
||||||
<div className={snapshotTitle}>{title}</div>
|
<div className={snapshotTitle}>{title}</div>
|
||||||
|
|||||||
@@ -141,6 +141,8 @@ export const TextSettings = () => {
|
|||||||
<EdgelessSnapshot
|
<EdgelessSnapshot
|
||||||
title={t['com.affine.settings.editorSettings.edgeless.text']()}
|
title={t['com.affine.settings.editorSettings.edgeless.text']()}
|
||||||
docName="text"
|
docName="text"
|
||||||
|
keyName="affine:edgeless-text"
|
||||||
|
flavour="affine:edgeless-text"
|
||||||
/>
|
/>
|
||||||
<SettingRow
|
<SettingRow
|
||||||
name={t['com.affine.settings.editorSettings.edgeless.text.color']()}
|
name={t['com.affine.settings.editorSettings.edgeless.text.color']()}
|
||||||
|
|||||||
Reference in New Issue
Block a user