From 34070d07fb3335d68388be79687f68e53a8bb334 Mon Sep 17 00:00:00 2001 From: Akhmed Ibragimov Date: Sat, 8 Jun 2024 01:52:40 +0300 Subject: [PATCH] Added deleteBlocks method and fixed deleting several blocks (#181) * added deleteBlocks method and fixed deleting several blocks after selection --- packages/core/editor/README.md | 8 +- .../editor/src/components/Editor/Editor.tsx | 4 +- .../contexts/YooptaContext/YooptaContext.tsx | 1 + .../editor/src/editor/blocks/deleteBlocks.ts | 96 ++++++ .../core/editor/src/editor/blocks/index.ts | 2 + packages/core/editor/src/editor/index.tsx | 2 + packages/core/editor/src/editor/types.ts | 2 + packages/development/src/pages/dev/index.tsx | 298 ------------------ 8 files changed, 112 insertions(+), 301 deletions(-) create mode 100644 packages/core/editor/src/editor/blocks/deleteBlocks.ts diff --git a/packages/core/editor/README.md b/packages/core/editor/README.md index 4459f9593..84a083be2 100644 --- a/packages/core/editor/README.md +++ b/packages/core/editor/README.md @@ -103,10 +103,16 @@ type YooEditor = { updateBlock: (id: string, data: Partial) => void; /** - * Deletes a block from the editor. + * Deletes block from the editor children. */ deleteBlock: (options?: DeleteBlockOptions) => void; + /** + * Deletes blocks from paths or blockIds + * + */ + deleteBlocks: (options?: DeleteBlocksOptions) => void; + /** * Duplicates a block within the editor. */ diff --git a/packages/core/editor/src/components/Editor/Editor.tsx b/packages/core/editor/src/components/Editor/Editor.tsx index 25fd73204..9327e3e7b 100644 --- a/packages/core/editor/src/components/Editor/Editor.tsx +++ b/packages/core/editor/src/components/Editor/Editor.tsx @@ -198,7 +198,7 @@ const Editor = ({ if (isAllBlocksSelected) { event.preventDefault(); - editor.deleteBlock({ deleteAll: true }); + editor.deleteBlocks({ deleteAll: true }); editor.setBlockSelected(null); resetSelectionState(); return; @@ -206,7 +206,7 @@ const Editor = ({ if (Array.isArray(editor.selectedBlocks) && editor.selectedBlocks?.length > 0) { event.preventDefault(); - editor.deleteBlock({ fromPaths: editor.selectedBlocks }); + editor.deleteBlocks({ paths: editor.selectedBlocks, focus: false }); editor.setBlockSelected(null); resetSelectionState(); return; diff --git a/packages/core/editor/src/contexts/YooptaContext/YooptaContext.tsx b/packages/core/editor/src/contexts/YooptaContext/YooptaContext.tsx index 2aff81bb2..cc7d64eae 100644 --- a/packages/core/editor/src/contexts/YooptaContext/YooptaContext.tsx +++ b/packages/core/editor/src/contexts/YooptaContext/YooptaContext.tsx @@ -18,6 +18,7 @@ const DEFAULT_HANDLERS: YooptaEditorContext = { moveBlock: () => undefined, splitBlock: () => undefined, deleteBlock: () => undefined, + deleteBlocks: () => undefined, toggleBlock: () => undefined, focusBlock: () => undefined, decreaseBlockDepth: () => undefined, diff --git a/packages/core/editor/src/editor/blocks/deleteBlocks.ts b/packages/core/editor/src/editor/blocks/deleteBlocks.ts new file mode 100644 index 000000000..c73ad4a47 --- /dev/null +++ b/packages/core/editor/src/editor/blocks/deleteBlocks.ts @@ -0,0 +1,96 @@ +import { createDraft, finishDraft } from 'immer'; +import { createEditor, Editor } from 'slate'; +import { withHistory } from 'slate-history'; +import { withReact } from 'slate-react'; +import { buildBlockData } from '../../components/Editor/utils'; +import { withShortcuts } from '../../extensions/shortcuts'; +import { findPluginBlockBySelectionPath } from '../../utils/findPluginBlockBySelectionPath'; +import { generateId } from '../../utils/generateId'; +import { YooEditor, YooptaEditorTransformOptions } from '../types'; +// // [TODO] Circular deps +// import { buildSlateEditor } from '../../utils/editorBuilders'; + +function buildSlateEditor(editor: YooEditor): Editor { + const slate = withShortcuts(editor, withHistory(withReact(createEditor()))); + return slate; +} + +export type DeleteBlocksOptions = Pick & { + blockIds?: string[]; + paths?: number[]; + deleteAll?: boolean; +}; + +export function deleteBlocks(editor: YooEditor, options: DeleteBlocksOptions = {}) { + const { deleteAll = false, paths, blockIds, focus = true } = options; + + if (deleteAll || Object.keys(editor.children).length === 1) { + editor.children = {}; + editor.blockEditorsMap = {}; + const block = buildBlockData({ id: generateId() }); + const slate = buildSlateEditor(editor); + + editor.children[block.id] = block; + editor.blockEditorsMap[block.id] = slate; + + editor.setSelection([0]); + editor.applyChanges(); + editor.emit('change', editor.children); + + if (focus) editor.focusBlock(block.id, { waitExecution: true }); + + return; + } + + if (Array.isArray(blockIds) && blockIds.length > 0) { + editor.children = createDraft(editor.children); + + blockIds.forEach((id) => { + delete editor.children[id]; + delete editor.blockEditorsMap[id]; + }); + + const blockDataIds = Object.keys(editor.children).sort((a, b) => + editor.children[a].meta.order > editor.children[b].meta.order ? 1 : -1, + ); + + blockDataIds.forEach((id, index) => { + editor.children[id].meta.order = index; + }); + + editor.children = finishDraft(editor.children); + editor.applyChanges(); + editor.emit('change', editor.children); + + if (focus) editor.focusBlock(blockDataIds[0], { waitExecution: true }); + return; + } + + if (Array.isArray(paths) && paths.length > 0) { + editor.children = createDraft(editor.children); + + paths.forEach((path) => { + const block = findPluginBlockBySelectionPath(editor, { at: [path] }); + if (block) { + delete editor.children[block.id]; + delete editor.blockEditorsMap[block.id]; + } + }); + + const blockDataIds = Object.keys(editor.children).sort((a, b) => + editor.children[a].meta.order > editor.children[b].meta.order ? 1 : -1, + ); + + blockDataIds.forEach((id, index) => { + editor.children[id].meta.order = index; + }); + + editor.children = finishDraft(editor.children); + editor.applyChanges(); + editor.emit('change', editor.children); + + if (focus) editor.focusBlock(blockDataIds[0], { waitExecution: true }); + + return; + } +} diff --git a/packages/core/editor/src/editor/blocks/index.ts b/packages/core/editor/src/editor/blocks/index.ts index cef05c564..097aa73e4 100644 --- a/packages/core/editor/src/editor/blocks/index.ts +++ b/packages/core/editor/src/editor/blocks/index.ts @@ -1,5 +1,6 @@ import { insertBlock } from './insertBlock'; import { deleteBlock } from './deleteBlock'; +import { deleteBlocks } from './deleteBlocks'; import { moveBlock } from './moveBlock'; import { focusBlock } from './focusBlock'; import { splitBlock } from './splitBlock'; @@ -22,6 +23,7 @@ export const Blocks = { updateBlock, toggleBlock, insertBlocks, + deleteBlocks, // [TODO] // updateBlocks }; diff --git a/packages/core/editor/src/editor/index.tsx b/packages/core/editor/src/editor/index.tsx index d2498d6ae..75a1f3b6f 100644 --- a/packages/core/editor/src/editor/index.tsx +++ b/packages/core/editor/src/editor/index.tsx @@ -17,6 +17,7 @@ import { toggleBlock } from './blocks/toggleBlock'; import { blur } from './core/blur'; import { focus } from './core/focus'; import { isFocused } from './core/isFocused'; +import { deleteBlocks } from './blocks/deleteBlocks'; // export const YooEditor = {} // export const BlockTransforms = {} @@ -46,6 +47,7 @@ export const createYooptaEditor = (): YooEditor => { applyChanges: () => {}, insertBlock: (...args) => insertBlock(editor, ...args), insertBlocks: (...args) => insertBlocks(editor, ...args), + deleteBlocks: (...args) => deleteBlocks(editor, ...args), deleteBlock: (...args) => deleteBlock(editor, ...args), duplicateBlock: (...args) => duplicateBlock(editor, ...args), toggleBlock: (...args) => toggleBlock(editor, ...args), diff --git a/packages/core/editor/src/editor/types.ts b/packages/core/editor/src/editor/types.ts index c6148f6e6..23a7fa46e 100644 --- a/packages/core/editor/src/editor/types.ts +++ b/packages/core/editor/src/editor/types.ts @@ -8,6 +8,7 @@ import { DeleteBlockOptions } from './blocks/deleteBlock'; import { DuplicateBlockOptions } from './blocks/duplicateBlock'; import { FocusBlockOptions } from './blocks/focusBlock'; import { ToggleBlockOptions } from './blocks/toggleBlock'; +import { DeleteBlocksOptions } from './blocks/deleteBlocks'; export type YooptaBlockPath = [number]; @@ -75,6 +76,7 @@ export type YooEditor = { splitBlock: (options?: YooptaEditorTransformOptions) => void; updateBlock: (id: string, data: Partial) => void; deleteBlock: (options?: DeleteBlockOptions) => void; + deleteBlocks: (options?: DeleteBlocksOptions) => void; duplicateBlock: (options?: DuplicateBlockOptions) => void; getBlock: (options?: YooptaEditorTransformOptions) => void; toggleBlock: (toBlockType: string, options?: ToggleBlockOptions) => void; diff --git a/packages/development/src/pages/dev/index.tsx b/packages/development/src/pages/dev/index.tsx index ee5e46c61..988491c5b 100644 --- a/packages/development/src/pages/dev/index.tsx +++ b/packages/development/src/pages/dev/index.tsx @@ -187,303 +187,6 @@ const TOOLS: Tools = { export type YooptaChildrenValue = Record; -const value = { - '9b291f39-d5d0-4927-9ae3-cca900937955': { - id: '9b291f39-d5d0-4927-9ae3-cca900937955', - value: [ - { - id: '1f973a32-90f6-45f6-9310-2de1da4f10a3', - type: 'accordion-list', - children: [ - { - id: 'd8e917e8-fe31-46bd-8076-226d2d76237d', - type: 'accordion-list-item', - children: [ - { - id: '76d6ba08-40b2-45da-80f9-17a954e3e82c', - type: 'accordion-list-item-heading', - children: [ - { - text: 'asdsad', - }, - ], - props: { - nodeType: 'block', - }, - }, - { - id: '6599fa62-0da4-4ca0-9279-551828ec9255', - type: 'accordion-list-item-content', - children: [ - { - text: '', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - props: { - nodeType: 'block', - isExpanded: true, - }, - }, - { - id: '705baea1-b0a0-4562-87d7-a0a61e38aa9b', - type: 'accordion-list-item', - children: [ - { - id: '18e8dba7-bb35-4914-968f-357c50a5a897', - type: 'accordion-list-item-heading', - children: [ - { - text: 'asdsadasadasds', - }, - ], - props: { - nodeType: 'block', - }, - }, - { - id: '951a8842-ae7a-495e-b574-9f9f0a497842', - type: 'accordion-list-item-content', - children: [ - { - text: '', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - props: { - nodeType: 'block', - isExpanded: true, - }, - }, - { - id: '80cee518-63e5-47ec-8455-49a5acfe27b3', - type: 'accordion-list-item', - children: [ - { - id: 'b9f6beed-968f-4636-a439-9575084fcd76', - type: 'accordion-list-item-heading', - children: [ - { - text: '', - }, - ], - props: { - nodeType: 'block', - }, - }, - { - id: '16ae9f42-459a-4eb9-a937-a8d44da19f00', - type: 'accordion-list-item-content', - children: [ - { - text: '', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - props: { - nodeType: 'block', - isExpanded: true, - }, - }, - ], - }, - ], - type: 'Accordion', - meta: { - order: 4, - depth: 0, - }, - }, - 'd670dc65-0dea-4c95-a806-46b912ca58eb': { - id: 'd670dc65-0dea-4c95-a806-46b912ca58eb', - value: [ - { - id: '48f7e7af-c173-4381-a28c-88ff538262ae', - type: 'paragraph', - children: [ - { - text: 'asdsad', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 6, - depth: 0, - }, - }, - 'c83f5cb4-2672-402e-960f-775ae57ef1a0': { - id: 'c83f5cb4-2672-402e-960f-775ae57ef1a0', - value: [ - { - id: 'e8fbbc87-31d6-4c0d-8292-5b170913274e', - type: 'paragraph', - children: [ - { - text: 'asdaasdsad', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 7, - depth: 0, - }, - }, - '2efa2d2e-1fdd-4c52-b4a6-4f49eb14b623': { - id: '2efa2d2e-1fdd-4c52-b4a6-4f49eb14b623', - value: [ - { - id: '3f8207f1-8a5f-4fa5-ba89-cec6b378d27f', - type: 'paragraph', - children: [ - { - text: 'sdasdsad', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 8, - depth: 0, - }, - }, - '55daff58-28e5-4b5e-be4e-3188c6873195': { - id: '55daff58-28e5-4b5e-be4e-3188c6873195', - value: [ - { - id: '64354934-4895-4bba-8432-d9fed11f7d90', - type: 'heading-one', - props: { - nodeType: 'block', - }, - children: [ - { - text: '', - }, - ], - }, - ], - type: 'HeadingOne', - meta: { - order: 5, - depth: 0, - }, - }, - 'd57ca7fd-2197-4296-86a4-3570e0dad3d3': { - id: 'd57ca7fd-2197-4296-86a4-3570e0dad3d3', - value: [ - { - id: '49d09142-5ed9-4780-8d20-1707ab7fb3bb', - type: 'paragraph', - children: [ - { - text: 'lk', - }, - ], - props: { - nodeType: 'block', - }, - }, - ], - type: 'Paragraph', - meta: { - order: 0, - depth: 0, - }, - }, - '3fd0ea93-13a4-4b83-8b00-72cac06c6ae6': { - id: '3fd0ea93-13a4-4b83-8b00-72cac06c6ae6', - type: 'Paragraph', - meta: { - order: 1, - depth: 0, - }, - value: [ - { - id: '49d09142-5ed9-4780-8d20-1707ab7fb3bb', - type: 'paragraph', - props: { - nodeType: 'block', - }, - children: [ - { - text: 'lj', - }, - ], - }, - ], - }, - 'a28fdfde-3504-4d7a-84d2-2da951a9e757': { - id: 'a28fdfde-3504-4d7a-84d2-2da951a9e757', - type: 'Paragraph', - meta: { - order: 2, - depth: 0, - }, - value: [ - { - id: '49d09142-5ed9-4780-8d20-1707ab7fb3bb', - type: 'paragraph', - props: { - nodeType: 'block', - }, - children: [ - { - text: 'lk', - }, - ], - }, - ], - }, - 'c5269d3b-3e7a-4fec-825b-74fb50ec5174': { - id: 'c5269d3b-3e7a-4fec-825b-74fb50ec5174', - type: 'Paragraph', - meta: { - order: 3, - depth: 0, - }, - value: [ - { - id: '49d09142-5ed9-4780-8d20-1707ab7fb3bb', - type: 'paragraph', - props: { - nodeType: 'block', - }, - children: [ - { - text: 'jk;', - }, - ], - }, - ], - }, -}; - const BasicExample = () => { const editor: YooEditor = useMemo(() => createYooptaEditor(), []); const rectangleSelectionRef = useRef(null); @@ -509,7 +212,6 @@ const BasicExample = () => { placeholder="Type / to open menu" tools={TOOLS} readOnly={readOnly} - value={value} style={{ width: 750, }}