diff --git a/.changeset/cool-lions-cry.md b/.changeset/cool-lions-cry.md new file mode 100644 index 0000000000..42c2cf9d00 --- /dev/null +++ b/.changeset/cool-lions-cry.md @@ -0,0 +1,5 @@ +--- +"@udecode/plate-toggle": patch +--- + +Toggle > Handle deleteForward before a non-selectable and deleteBackward after a non-selectable diff --git a/packages/toggle/src/transforms/index.ts b/packages/toggle/src/transforms/index.ts index 6715cdf8d5..e1b19eeaa9 100644 --- a/packages/toggle/src/transforms/index.ts +++ b/packages/toggle/src/transforms/index.ts @@ -2,4 +2,6 @@ * @file Automatically generated by barrelsby. */ +export * from './moveCurrentBlockAfterPreviousSelectable'; +export * from './moveNextSelectableAfterCurrentBlock'; export * from './openNextToggles'; diff --git a/packages/toggle/src/transforms/moveCurrentBlockAfterPreviousSelectable.ts b/packages/toggle/src/transforms/moveCurrentBlockAfterPreviousSelectable.ts new file mode 100644 index 0000000000..5845d57389 --- /dev/null +++ b/packages/toggle/src/transforms/moveCurrentBlockAfterPreviousSelectable.ts @@ -0,0 +1,37 @@ +import { + getBlockAbove, + getPointBefore, + getPreviousNode, + isElement, + isSelectionAtBlockStart, + moveNodes, + PlateEditor, +} from '@udecode/plate-common'; + +import { isInClosedToggle } from '../queries'; + +// Return false only if the all previous blocks are not selectable +export const moveCurrentBlockAfterPreviousSelectable = ( + editor: PlateEditor +): boolean | undefined => { + const { selection } = editor; + if (!selection) return; + const aboveBlock = getBlockAbove(editor); + if (!aboveBlock) return; + if (!isSelectionAtBlockStart(editor)) return; + const beforePoint = getPointBefore(editor, selection); + if (!beforePoint) return; + const blockBefore = getBlockAbove(editor, { at: beforePoint }); + if (!blockBefore) return; + if (!isInClosedToggle(editor, blockBefore[0].id)) return; // We're already after a selectable then + const previousSelectableBlock = getPreviousNode(editor, { + match: (node) => + isElement(node) && !isInClosedToggle(editor, node.id as string), + }); + if (!previousSelectableBlock) return false; + const afterSelectableBlock = [previousSelectableBlock[1][0] + 1]; + moveNodes(editor, { + at: aboveBlock[1], + to: afterSelectableBlock, + }); +}; diff --git a/packages/toggle/src/transforms/moveNextSelectableAfterCurrentBlock.ts b/packages/toggle/src/transforms/moveNextSelectableAfterCurrentBlock.ts new file mode 100644 index 0000000000..a16895a5ba --- /dev/null +++ b/packages/toggle/src/transforms/moveNextSelectableAfterCurrentBlock.ts @@ -0,0 +1,35 @@ +import { + getBlockAbove, + getNextNode, + getPointAfter, + isElement, + isSelectionAtBlockEnd, + moveNodes, + PlateEditor, +} from '@udecode/plate-common'; + +import { isInClosedToggle } from '../queries'; + +// Return false only if all next blocks are not selectable +export const moveNextSelectableAfterCurrentBlock = (editor: PlateEditor) => { + const { selection } = editor; + if (!selection) return; + const aboveBlock = getBlockAbove(editor); + if (!aboveBlock) return; + if (!isSelectionAtBlockEnd(editor)) return; + const afterPoint = getPointAfter(editor, selection); + if (!afterPoint) return; + const blockAfter = getBlockAbove(editor, { at: afterPoint }); + if (!blockAfter) return; + if (!isInClosedToggle(editor, blockAfter[0].id)) return; // We're already before a selectable then + const nextSelectableBlock = getNextNode(editor, { + match: (node) => + isElement(node) && !isInClosedToggle(editor, node.id as string), + }); + if (!nextSelectableBlock) return false; + const afterCurrentBlock = [aboveBlock[1][0] + 1]; + moveNodes(editor, { + at: nextSelectableBlock[1], + to: afterCurrentBlock, + }); +}; diff --git a/packages/toggle/src/withToggle.ts b/packages/toggle/src/withToggle.ts index c171949781..a067385d19 100644 --- a/packages/toggle/src/withToggle.ts +++ b/packages/toggle/src/withToggle.ts @@ -10,6 +10,10 @@ import { indent, TIndentElement } from '@udecode/plate-indent'; import { getLastEntryEnclosedInToggle, isInClosedToggle } from './queries'; import { isToggleOpen } from './toggle-controller-store'; +import { + moveCurrentBlockAfterPreviousSelectable, + moveNextSelectableAfterCurrentBlock, +} from './transforms'; import { ELEMENT_TOGGLE } from './types'; export const withToggle = < @@ -18,7 +22,7 @@ export const withToggle = < >( editor: E ) => { - const { insertBreak, isSelectable } = editor; + const { insertBreak, isSelectable, deleteBackward, deleteForward } = editor; editor.isSelectable = (element) => { if (isNode(element) && isInClosedToggle(editor, element.id as string)) @@ -26,6 +30,20 @@ export const withToggle = < return isSelectable(element); }; + editor.deleteBackward = (unit) => { + if ( + moveCurrentBlockAfterPreviousSelectable(editor as PlateEditor) === false + ) + return; + deleteBackward(unit); + }; + + editor.deleteForward = (unit) => { + if (moveNextSelectableAfterCurrentBlock(editor as PlateEditor) === false) + return; + deleteForward(unit); + }; + editor.insertBreak = () => { // If we are inserting a break in a toggle: // If the toggle is open @@ -57,9 +75,13 @@ export const withToggle = < insertBreak(); if (lastEntryEnclosedInToggle) { + const newlyInsertedTogglePath = [currentBlockEntry[1][0] + 1]; + const afterLastEntryEncloseInToggle = [ + lastEntryEnclosedInToggle[1][0] + 1, + ]; moveNodes(editor, { - at: [currentBlockEntry[1][0] + 1], // Newly inserted toggle - to: [lastEntryEnclosedInToggle[1][0] + 1], // Right after the last enclosed element + at: newlyInsertedTogglePath, + to: afterLastEntryEncloseInToggle, }); } }