feat: the feature of delete block

This commit is contained in:
qinluhe 2023-03-23 16:13:03 +08:00
parent a38c213744
commit c6c97f7c83
5 changed files with 84 additions and 28 deletions

View File

@ -1,15 +1,26 @@
import { BaseEditor, BaseSelection, Descendant } from "slate";
import { BaseEditor, BaseSelection, Descendant, Editor, Transforms } from "slate";
import { TreeNode } from '$app/block_editor/view/tree_node';
import { Operation } from "$app/block_editor/core/operation";
import { TextBlockSelectionManager } from './text_selection';
import { BlockType } from "@/appflowy_app/interfaces";
import { ReactEditor } from "slate-react";
export class TextBlockManager {
public selectionManager: TextBlockSelectionManager;
private editorMap: Map<string, BaseEditor & ReactEditor> = new Map();
constructor(private rootId: string, private operation: Operation) {
this.selectionManager = new TextBlockSelectionManager();
}
register(id: string, editor: BaseEditor & ReactEditor) {
this.editorMap.set(id, editor);
}
unregister(id: string) {
this.editorMap.delete(id);
}
setSelection(node: TreeNode, selection: BaseSelection) {
// console.log(node.id, selection);
this.selectionManager.setSelection(node.id, selection)
@ -22,19 +33,41 @@ export class TextBlockManager {
deleteNode(node: TreeNode) {
if (node.type !== BlockType.TextBlock) {
this.operation.updateNode(node.id, ['type'], BlockType.TextBlock);
this.operation.updateNode(node.id, ['data'], { content: node.data.content });
return;
}
if (node.parent!.id !== this.rootId) {
if (!node.block.next && node.parent!.id !== this.rootId) {
const newParent = node.parent!.parent!;
const newPrev = node.parent;
this.operation.moveNode(node.id, newParent.id, newPrev?.id || '');
return;
}
if (!node.prevLine) return;
const retainData = node.prevLine.data.content;
const editor = this.editorMap.get(node.prevLine.id);
if (editor) {
const index = retainData.length - 1;
const anchor = {
path: [0, index],
offset: retainData[index].text.length,
};
const selection = {
anchor,
focus: {...anchor}
};
ReactEditor.focus(editor);
Transforms.select(editor, selection);
}
this.operation.updateNode(node.prevLine.id, ['data', 'content'], [
...node.prevLine.data.content,
...retainData,
...node.data.content,
]);
this.operation.deleteNode(node.id);
}
splitNode(node: TreeNode, editor: BaseEditor) {

View File

@ -118,9 +118,10 @@ export class BlockChain {
remove(blockId: string) {
const block = this.getBlock(blockId);
if (!block) return;
const oldParentId = block.parent?.id;
block.remove();
this.map.delete(block.id);
this.onBlockChange('delete', { block });
this.onBlockChange('remove', { oldParentId });
return block;
}

View File

@ -87,27 +87,28 @@ export class RenderTree {
if (!block) return null;
const node = this.createNode(block);
if (!node) return null;
if (shouldUpdateChildren) {
const children: TreeNode[] = [];
let childBlock = block.firstChild;
while(childBlock) {
const child = this.createNode(childBlock);
child.update(childBlock, child.children);
children.push(child);
childBlock = childBlock.next;
}
node.update(block, children);
node?.reRender();
node?.children.forEach(child => {
child.reRender();
})
} else {
node.update(block, node.children);
node?.reRender();
if (!shouldUpdateChildren) {
node.update(node.block, node.children);
node.reRender();
return;
}
const children: TreeNode[] = [];
let childBlock = block.firstChild;
while (childBlock) {
const child = this.createNode(childBlock);
child.update(childBlock, child.children);
children.push(child);
childBlock = childBlock.next;
}
node.update(block, children);
node.reRender();
node.children.forEach(child => {
child.reRender();
});
}
onBlockChange(command: string, data: BlockChangeProps) {
@ -119,6 +120,9 @@ export class RenderTree {
case 'update':
this.forceUpdate(block!.id);
break;
case 'remove':
if (oldParentId) this.forceUpdate(oldParentId, true);
break;
case 'move':
if (oldParentId) this.forceUpdate(oldParentId, true);
if (block?.parent) this.forceUpdate(block.parent.id, true);
@ -127,7 +131,7 @@ export class RenderTree {
default:
break;
}
}
updateSelections(selections: string[]) {

View File

@ -36,6 +36,7 @@ export class TreeNode {
}
update(block: Block, children: TreeNode[]) {
this.type = block.type;
this.data = block.data;
this.children = [];
children.forEach(child => {

View File

@ -1,7 +1,7 @@
import { TreeNode } from "@/appflowy_app/block_editor/view/tree_node";
import { triggerHotkey } from "@/appflowy_app/utils/slate/hotkey";
import { useCallback, useContext, useLayoutEffect, useState } from "react";
import { Transforms, createEditor, Descendant } from 'slate';
import { useCallback, useContext, useEffect, useLayoutEffect, useState } from "react";
import { Transforms, createEditor, Descendant, Range } from 'slate';
import { ReactEditor, withReact } from 'slate-react';
import { TextBlockContext } from '$app/utils/slate/context';
@ -50,7 +50,14 @@ export function useTextBlock({
return;
}
case 'Backspace': {
console.log(editor.selection)
if (!editor.selection) return;
const { anchor } = editor.selection;
const isCollapase = Range.isCollapsed(editor.selection);
if (isCollapase && anchor.offset === 0 && anchor.path.toString() === '0,0') {
event.stopPropagation();
event.preventDefault();
textBlockManager?.deleteNode(node);
}
}
}
@ -64,7 +71,17 @@ export function useTextBlock({
editor.children = value;
Transforms.collapse(editor);
useEffect(() => {
textBlockManager?.register(node.id, editor);
return () => {
textBlockManager?.unregister(node.id);
}
}, [ editor ])
useLayoutEffect(() => {
let timer: NodeJS.Timeout;
if (focusId === node.id && selection) {
ReactEditor.focus(editor);