Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 4x 5x 4x 5x 4x 4x 5x 5x 10x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x | import {Editor, Element as SlateElement, Node, Path, Transforms} from 'slate';
import type {CustomElement, CustomText} from '../types';
const createParagraph = (): CustomElement => ({type: 'p', children: [{text: ''}] as CustomText[]});
const isAtEmptyParagraph = (entry: [CustomElement, Path] | null): boolean =>
!!entry && (entry[0] as any).type === 'p' && Node.string(entry[0]) === '';
/**
* If the caret/selection is on the very first block and that block is a void,
* prepend an empty paragraph and move the caret into it.
*/
export const insertPAboveLeadingVoid = (editor: Editor): boolean => {
const {selection} = editor;
if (!selection) return false;
if (selection.anchor.path[0] !== 0) return false;
const first = (editor as any).children?.[0];
if (!SlateElement.isElement(first)) return false;
if (!Editor.isVoid(editor, first)) return false;
Transforms.insertNodes(editor, createParagraph(), {at: [0]});
Transforms.select(editor, Editor.start(editor, [0]));
return true;
};
/**
* Insert a void block element at the current selection. Mirrors the pattern
* shared by `<embed>`, `<hr>`, `<file>`, and any future block voids:
*
* 1. If the caret is in an empty paragraph, replace it with the void.
* 2. Otherwise insert the void at the caret.
* 3. Ensure there's a paragraph immediately after, and place the caret in it.
*
* Returns the inserted node (echoing the input), or `null` if there was no
* selection and Slate refused to insert.
*/
export const insertVoidBlock = <T extends CustomElement>(editor: Editor, element: T): T | null => {
const {selection} = editor;
const currentBlockEntry = (
selection
? Editor.above(editor, {
at: Editor.unhangRange(editor, selection),
match: (node) => SlateElement.isElement(node) && Editor.isBlock(editor, node),
mode: 'lowest',
})
: null
) as [CustomElement, Path] | null;
let insertedPath: Path | null = null;
if (isAtEmptyParagraph(currentBlockEntry)) {
const [, path] = currentBlockEntry!;
Transforms.removeNodes(editor, {at: path});
Transforms.insertNodes(editor, element, {at: path, select: true});
insertedPath = path;
} else E{
Transforms.insertNodes(editor, element, {select: true});
// After insertion, the new node is wherever Slate placed it. Resolve via
// the editor's current selection.
const after = editor.selection;
if (after) insertedPath = [after.anchor.path[0]];
}
Iif (!insertedPath) return null;
const afterPath = Path.next(insertedPath);
Eif (!Node.has(editor, afterPath)) {
Transforms.insertNodes(editor, createParagraph(), {at: afterPath});
}
Eif (Node.has(editor, afterPath)) {
Transforms.select(editor, Editor.start(editor, afterPath));
}
return element;
};
|