Merge pull request #265 from toeverything/fix/text-with-root-id

Fix/text with root id
This commit is contained in:
DarkSky
2022-08-16 18:55:19 +08:00
committed by GitHub
2 changed files with 87 additions and 47 deletions

View File

@@ -2,13 +2,11 @@ import { useState } from 'react';
import { CustomText, TextProps } from '@toeverything/components/common'; import { CustomText, TextProps } from '@toeverything/components/common';
import { import {
mergeGroup, BlockPendantProvider,
RenderBlockChildren, RenderBlockChildren,
splitGroup, splitGroup,
supportChildren, supportChildren,
unwrapGroup,
useOnSelect, useOnSelect,
BlockPendantProvider,
} from '@toeverything/components/editor-core'; } from '@toeverything/components/editor-core';
import { styled } from '@toeverything/components/ui'; import { styled } from '@toeverything/components/ui';
import { Protocol } from '@toeverything/datasource/db-service'; import { Protocol } from '@toeverything/datasource/db-service';
@@ -69,24 +67,36 @@ export const TextView = ({
const { contentBeforeSelection, contentAfterSelection } = splitContents; const { contentBeforeSelection, contentAfterSelection } = splitContents;
const before = [...contentBeforeSelection.content]; const before = [...contentBeforeSelection.content];
const after = [...contentAfterSelection.content]; const after = [...contentAfterSelection.content];
const _nextBlockChildren = await block.children(); const nextBlockChildren = await block.children();
const _nextBlock = await editor.createBlock('text'); const nextBlock = await editor.createBlock('text');
await _nextBlock.setProperty('text', { if (!nextBlock) {
throw new Error('Failed to create text block');
}
await nextBlock.setProperty('text', {
value: after as CustomText[], value: after as CustomText[],
}); });
_nextBlock.append(..._nextBlockChildren);
block.removeChildren();
await block.setProperty('text', { await block.setProperty('text', {
value: before as CustomText[], value: before as CustomText[],
}); });
await block.after(_nextBlock);
editor.selectionManager.activeNodeByNodeId(_nextBlock.id); if (editor.getRootBlockId() === block.id) {
// If the block is the root block,
// new block can not append as next sibling,
// all new blocks should be append as children.
await block.insert(0, [nextBlock]);
editor.selectionManager.activeNodeByNodeId(nextBlock.id);
return true;
}
await nextBlock.append(...nextBlockChildren);
await block.removeChildren();
await block.after(nextBlock);
editor.selectionManager.activeNodeByNodeId(nextBlock.id);
return true; return true;
}; };
const onBackspace: TextProps['handleBackSpace'] = async props => { const onBackspace: TextProps['handleBackSpace'] = editor.withBatch(
return await editor.withSuspend(async () => { async props => {
const { isCollAndStart } = props; const { isCollAndStart } = props;
if (!isCollAndStart) { if (!isCollAndStart) {
return false; return false;
@@ -95,20 +105,27 @@ export const TextView = ({
await block.setType('text'); await block.setType('text');
return true; return true;
} }
if (editor.getRootBlockId() === block.id) {
// Can not delete
return false;
}
const parentBlock = await block.parent(); const parentBlock = await block.parent();
if (!parentBlock) { if (!parentBlock) {
return false; return false;
} }
const preParent = await parentBlock.previousSibling(); const preParent = await parentBlock.previousSibling();
if (Protocol.Block.Type.group === parentBlock.type) { if (
Protocol.Block.Type.group === parentBlock.type ||
editor.getRootBlockId() === parentBlock.id
) {
const children = await block.children(); const children = await block.children();
const preNode = await block.physicallyPerviousSibling(); const preNode = await block.physicallyPerviousSibling();
// FIXME support children do not means has textBlock // FIXME support children do not means has textBlock
// TODO: abstract this part of code // TODO: abstract this part of code
if (preNode) { if (preNode) {
if (supportChildren(preNode)) { if (supportChildren(preNode)) {
editor.suspend(true);
await editor.selectionManager.activePreviousNode( await editor.selectionManager.activePreviousNode(
block.id, block.id,
'end' 'end'
@@ -127,7 +144,6 @@ export const TextView = ({
} }
await preNode.append(...children); await preNode.append(...children);
await block.remove(); await block.remove();
editor.suspend(false);
} else { } else {
// TODO: point does not clear // TODO: point does not clear
await editor.selectionManager.activePreviousNode( await editor.selectionManager.activePreviousNode(
@@ -194,8 +210,9 @@ export const TextView = ({
); );
} }
return true; return true;
}); }
}; );
const handleConvert = async ( const handleConvert = async (
toType: string, toType: string,
options?: Record<string, unknown> options?: Record<string, unknown>

View File

@@ -2,40 +2,40 @@
import HotKeys from 'hotkeys-js'; import HotKeys from 'hotkeys-js';
import LRUCache from 'lru-cache'; import LRUCache from 'lru-cache';
import { services } from '@toeverything/datasource/db-service'; import type { PatchNode } from '@toeverything/components/ui';
import type { import type {
BlockFlavors, BlockFlavors,
ReturnEditorBlock, ReturnEditorBlock,
UpdateEditorBlock, UpdateEditorBlock,
} from '@toeverything/datasource/db-service'; } from '@toeverything/datasource/db-service';
import type { PatchNode } from '@toeverything/components/ui'; import { services } from '@toeverything/datasource/db-service';
import { AsyncBlock } from './block';
import type { WorkspaceAndBlockId } from './block';
import type { BaseView } from './views/base-view';
import { SelectionManager } from './selection';
import { Hooks, PluginManager } from './plugin';
import { EditorCommands } from './commands';
import {
Virgo,
HooksRunner,
PluginHooks,
PluginCreator,
StorageManager,
VirgoSelection,
PluginManagerInterface,
} from './types';
import { KeyboardManager } from './keyboard';
import { MouseManager } from './mouse';
import { ScrollManager } from './scroll';
import assert from 'assert';
import { domToRect, last, Point, sleep } from '@toeverything/utils';
import { Commands } from '@toeverything/datasource/commands'; import { Commands } from '@toeverything/datasource/commands';
import { domToRect, last, Point, sleep } from '@toeverything/utils';
import assert from 'assert';
import type { WorkspaceAndBlockId } from './block';
import { AsyncBlock } from './block';
import { BlockHelper } from './block/block-helper';
import { BrowserClipboard } from './clipboard/browser-clipboard'; import { BrowserClipboard } from './clipboard/browser-clipboard';
import { ClipboardPopulator } from './clipboard/clipboard-populator'; import { ClipboardPopulator } from './clipboard/clipboard-populator';
import { BlockHelper } from './block/block-helper'; import { EditorCommands } from './commands';
import { DragDropManager } from './drag-drop';
import { EditorConfig } from './config'; import { EditorConfig } from './config';
import { DragDropManager } from './drag-drop';
import { KeyboardManager } from './keyboard';
import { MouseManager } from './mouse';
import { Hooks, PluginManager } from './plugin';
import { ScrollManager } from './scroll';
import { SelectionManager } from './selection';
import {
HooksRunner,
PluginCreator,
PluginHooks,
PluginManagerInterface,
StorageManager,
Virgo,
VirgoSelection,
} from './types';
import type { BaseView } from './views/base-view';
export interface EditorCtorProps { export interface EditorCtorProps {
workspace: string; workspace: string;
@@ -148,18 +148,41 @@ export class Editor implements Virgo {
public get container() { public get container() {
return this.ui_container; return this.ui_container;
} }
// preference to use withSuspend
/**
* Use it discreetly.
* Preference to use {@link withBatch}
*/
public suspend(flag: boolean) { public suspend(flag: boolean) {
services.api.editorBlock.suspend(this.workspace, flag); services.api.editorBlock.suspend(this.workspace, flag);
} }
public async withSuspend<T extends (...args: any[]) => any>( // TODO support suspend recursion
private _isSuspend = false;
public withBatch<T extends (...args: any[]) => Promise<any>>(fn: T): T {
return (async (...args) => {
if (this._isSuspend) {
console.warn(
'The editor currently has suspend! Please do not call batch method repeatedly!'
);
}
this._isSuspend = true;
services.api.editorBlock.suspend(this.workspace, true);
const result = await fn(...args);
services.api.editorBlock.suspend(this.workspace, false);
this._isSuspend = false;
return result;
}) as T;
}
/**
* Use it discreetly.
* Preference to use {@link withBatch}
*/
public async batch<T extends (...args: any[]) => any>(
fn: T fn: T
): Promise<Awaited<ReturnType<T>>> { ): Promise<Awaited<ReturnType<T>>> {
services.api.editorBlock.suspend(this.workspace, true); return this.withBatch(fn)();
const result = await fn();
services.api.editorBlock.suspend(this.workspace, false);
return result;
} }
public setReactRenderRoot(props: { public setReactRenderRoot(props: {