mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: the feature of delete block
This commit is contained in:
parent
a38c213744
commit
c6c97f7c83
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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[]) {
|
||||
|
@ -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 => {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user