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 75 76 77 78 79 80 81 82 83 84 85 86 | 1x 1x 1x 1x 2x 1x 1x 7x 7x 6x 6x 6x 6x 6x 6x 6x 6x 6x 6x 1x 4x 4x 4x 9x 4x 4x 4x 4x 1x 5x 5x 2x 2x 1x 1x 5x 2x 2x 1x 1x 5x 5x | import {Editor, type Node, Path, Range, Text, Transforms} from 'slate';
import type {CustomElement} from '../types';
/** Sum of plain-text character counts across every `Text` node in the doc. */
export const docPlainTextLength = (editor: Editor): number => {
let total = 0;
for (const [node] of Editor.nodes(editor, {at: [], match: Text.isText})) {
total += (node as {text: string}).text.length;
}
return total;
};
/** Returns `true` when the selection is an expanded range that covers the
* entire user-visible document. */
export const isFullDocSelected = (editor: Editor): boolean => {
const {selection} = editor;
if (!selection || Range.isCollapsed(selection)) return false;
const children = editor.children;
Iif (!children.length) return false;
const firstIdx = 0;
const lastIdx = children.length - 1;
Iif (lastIdx < firstIdx) return false;
let docStart: ReturnType<typeof Editor.start>;
let docEnd: ReturnType<typeof Editor.end>;
try {
docStart = Editor.start(editor, [firstIdx]);
docEnd = Editor.end(editor, [lastIdx]);
} catch {
return false;
}
const [selStart, selEnd] = Range.edges(selection);
return (
Path.equals(selStart.path, docStart.path) &&
selStart.offset === docStart.offset &&
Path.equals(selEnd.path, docEnd.path) &&
selEnd.offset === docEnd.offset
);
};
export const resetDocumentContent = (editor: Editor, text = ''): void => {
Editor.withoutNormalizing(editor, () => {
const firstIdx = 0;
for (let i = editor.children.length - 1; i >= firstIdx; i--) {
Transforms.removeNodes(editor, {at: [i]});
}
const paragraph: CustomElement = {type: 'p', children: [{text}]};
Transforms.insertNodes(editor, paragraph, {at: [firstIdx]});
const caret = text ? Editor.end(editor, [firstIdx]) : Editor.start(editor, [firstIdx]);
Transforms.select(editor, caret);
});
};
export interface SelectAllGuardHooks {
onDelete?: () => boolean;
onReplaceWithText?: (text: string) => boolean;
onReplaceWithFragment?: (fragment: Node[]) => boolean;
}
export const withSelectAllGuard = <T extends Editor>(editor: T, hooks: SelectAllGuardHooks): T => {
const {deleteFragment, insertText, insertFragment} = editor;
editor.deleteFragment = (direction) => {
Eif (isFullDocSelected(editor)) {
if (hooks.onDelete?.()) return;
resetDocumentContent(editor);
return;
}
deleteFragment(direction);
};
editor.insertText = (text) => {
Eif (text && isFullDocSelected(editor)) {
if (hooks.onReplaceWithText?.(text)) return;
resetDocumentContent(editor, text);
return;
}
insertText(text);
};
editor.insertFragment = (fragment) => {
if (isFullDocSelected(editor)) {
if (hooks.onReplaceWithFragment?.(fragment)) return;
resetDocumentContent(editor);
}
insertFragment(fragment);
};
return editor;
};
|