Просмотр исходного кода

feat: the feature of delete block

qinluhe 2 лет назад
Родитель
Сommit
c6c97f7c83

+ 36 - 3
frontend/appflowy_tauri/src/appflowy_app/block_editor/blocks/text_block/index.ts

@@ -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) {

+ 2 - 1
frontend/appflowy_tauri/src/appflowy_app/block_editor/core/block_chain.ts

@@ -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;
   }
 

+ 22 - 18
frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree.ts

@@ -87,27 +87,28 @@ export class RenderTree {
     if (!block) return null;
     const node = this.createNode(block);
     if (!node) return null;
+    if (!shouldUpdateChildren) {
+      node.update(node.block, node.children);
+      node.reRender();
+      return;
+    }
 
-    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;
-      }
+    const children: TreeNode[] = [];
+    let childBlock = block.firstChild;
 
-      node.update(block, children);
-      node?.reRender();
-      node?.children.forEach(child => {
-        child.reRender();
-      })
-    } else {
-      node.update(block, node.children);
-      node?.reRender();
+    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[]) {

+ 1 - 0
frontend/appflowy_tauri/src/appflowy_app/block_editor/view/tree_node.ts

@@ -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 => {

+ 20 - 3
frontend/appflowy_tauri/src/appflowy_app/components/block/TextBlock/index.hooks.ts

@@ -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);