mirror of
https://github.com/facebook/lexical.git
synced 2025-08-06 08:30:33 +08:00
Add get{Current/Latest}EditorContent() (#682)
This commit is contained in:
@ -43,7 +43,7 @@ export default function useTypeahead(editor: OutlineEditor): void {
|
||||
// Monitor entered text
|
||||
useEffect(() => {
|
||||
return editor.addListener('update', (viewModel) => {
|
||||
const text = editor.getTextContent();
|
||||
const text = editor.getCurrentTextContent();
|
||||
setText(text);
|
||||
});
|
||||
}, [editor]);
|
||||
|
@ -41,7 +41,7 @@ export function useCharacterLimit(
|
||||
const Segmenter = Intl.Segmenter;
|
||||
let offsetUtf16 = 0;
|
||||
let offset = 0;
|
||||
const text = editor.getTextContent();
|
||||
const text = editor.getCurrentTextContent();
|
||||
if (typeof Segmenter === 'function') {
|
||||
const segmenter = new Segmenter();
|
||||
const graphemes = segmenter.segment(text);
|
||||
@ -79,7 +79,7 @@ export function useCharacterLimit(
|
||||
useEffect(() => {
|
||||
editor.registerNodeType('overflow', OverflowNode);
|
||||
(() => {
|
||||
const textLength = strlen(editor.getTextContent());
|
||||
const textLength = strlen(editor.getCurrentTextContent());
|
||||
const diff = maxCharacters - textLength;
|
||||
remainingCharacters(diff);
|
||||
execute();
|
||||
@ -90,7 +90,7 @@ export function useCharacterLimit(
|
||||
'update',
|
||||
(viewModel: ViewModel, dirtyNodes: Set<NodeKey> | null) => {
|
||||
const isComposing = editor.isComposing();
|
||||
const text = editor.getTextContent();
|
||||
const text = editor.getCurrentTextContent();
|
||||
const utf16TextLength = text.length;
|
||||
const hasDirtyNodes = dirtyNodes !== null && dirtyNodes.size > 0;
|
||||
if (
|
||||
@ -99,7 +99,7 @@ export function useCharacterLimit(
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const textLength = strlen(editor.getTextContent());
|
||||
const textLength = strlen(editor.getCurrentTextContent());
|
||||
const textLengthAboveThreshold =
|
||||
textLength > maxCharacters ||
|
||||
(lastTextLength !== null && lastTextLength > maxCharacters);
|
||||
|
@ -507,6 +507,32 @@ describe('OutlineEditor tests', () => {
|
||||
expect(parsedSelection.focus.key).toEqual(parsedText.__key);
|
||||
});
|
||||
});
|
||||
|
||||
it('getCurrentTextContent() / getLatestTextContent()', async () => {
|
||||
editor.update((view: View) => {
|
||||
const root = view.getRoot();
|
||||
const paragraph = createParagraphNode();
|
||||
const text1 = createTextNode('1');
|
||||
root.append(paragraph);
|
||||
paragraph.append(text1);
|
||||
});
|
||||
editor.update((view: View) => {
|
||||
const root = view.getRoot();
|
||||
const paragraph = root.getFirstChild();
|
||||
const text2 = createTextNode('2');
|
||||
paragraph.append(text2);
|
||||
});
|
||||
|
||||
expect(editor.getCurrentTextContent()).toBe('');
|
||||
expect(
|
||||
editor.getLatestTextContent((text) => {
|
||||
expect(text).toBe('12');
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.resolve();
|
||||
expect(editor.getCurrentTextContent()).toBe('12');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Node children', () => {
|
||||
@ -575,7 +601,7 @@ describe('OutlineEditor tests', () => {
|
||||
textToKey.set(previousText, textNode.__key);
|
||||
}
|
||||
});
|
||||
expect(editor.getTextContent()).toBe(previous.join(''));
|
||||
expect(editor.getCurrentTextContent()).toBe(previous.join(''));
|
||||
|
||||
// Next editor state
|
||||
const previousSet = new Set(previous);
|
||||
@ -609,7 +635,7 @@ describe('OutlineEditor tests', () => {
|
||||
});
|
||||
});
|
||||
// Expect text content + HTML to be correct
|
||||
expect(editor.getTextContent()).toBe(next.join(''));
|
||||
expect(editor.getCurrentTextContent()).toBe(next.join(''));
|
||||
expect(container.innerHTML).toBe(
|
||||
`<div contenteditable="true" data-outline-editor="true"><p>${
|
||||
next.length > 0
|
||||
|
@ -118,13 +118,13 @@ describe('OutlineTextNode tests', () => {
|
||||
view.getRoot().getFirstChild().append(textNode);
|
||||
});
|
||||
|
||||
expect(editor.getTextContent()).toBe('Text');
|
||||
expect(editor.getCurrentTextContent()).toBe('Text');
|
||||
|
||||
// Make sure that the editor content is still set after further reconciliations
|
||||
await update((view) => {
|
||||
view.markNodeAsDirty(view.getNodeByKey(nodeKey));
|
||||
});
|
||||
expect(editor.getTextContent()).toBe('Text');
|
||||
expect(editor.getCurrentTextContent()).toBe('Text');
|
||||
});
|
||||
|
||||
test('inert nodes', async () => {
|
||||
@ -140,13 +140,13 @@ describe('OutlineTextNode tests', () => {
|
||||
view.getRoot().getFirstChild().append(textNode);
|
||||
});
|
||||
|
||||
expect(editor.getTextContent()).toBe('');
|
||||
expect(editor.getCurrentTextContent()).toBe('');
|
||||
|
||||
// Make sure that the editor content is still empty after further reconciliations
|
||||
await update((view) => {
|
||||
view.markNodeAsDirty(view.getNodeByKey(nodeKey));
|
||||
});
|
||||
expect(editor.getTextContent()).toBe('');
|
||||
expect(editor.getCurrentTextContent()).toBe('');
|
||||
});
|
||||
|
||||
test('prepend node', async () => {
|
||||
@ -161,7 +161,7 @@ describe('OutlineTextNode tests', () => {
|
||||
previousTextNode.insertBefore(textNode);
|
||||
});
|
||||
|
||||
expect(editor.getTextContent()).toBe('Hello World');
|
||||
expect(editor.getCurrentTextContent()).toBe('Hello World');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -107,6 +107,22 @@ export type ListenerType =
|
||||
| 'root'
|
||||
| 'decorator';
|
||||
|
||||
let isPreparingPendingViewUpdate = false;
|
||||
|
||||
export function asyncErrorOnPreparingPendingViewUpdate(
|
||||
fnName: 'Editor.getLatestTextContent()',
|
||||
): void {
|
||||
if (
|
||||
isPreparingPendingViewUpdate &&
|
||||
fnName === 'Editor.getLatestTextContent()'
|
||||
) {
|
||||
invariant(
|
||||
false,
|
||||
'Editor.getLatestTextContent() can be asynchronous and cannot be used within Editor.update()',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function resetEditor(
|
||||
editor: OutlineEditor,
|
||||
prevRootElement: null | HTMLElement,
|
||||
@ -167,6 +183,7 @@ function updateEditor(
|
||||
viewModelWasCloned = true;
|
||||
}
|
||||
|
||||
isPreparingPendingViewUpdate = true;
|
||||
const error = preparePendingViewUpdate(
|
||||
pendingViewModel,
|
||||
updateFn,
|
||||
@ -174,6 +191,7 @@ function updateEditor(
|
||||
markAllTextNodesAsDirty,
|
||||
editor,
|
||||
);
|
||||
isPreparingPendingViewUpdate = false;
|
||||
|
||||
if (error !== null) {
|
||||
// Report errors
|
||||
@ -326,9 +344,17 @@ class BaseOutlineEditor {
|
||||
getRootElement(): null | HTMLElement {
|
||||
return this._rootElement;
|
||||
}
|
||||
getTextContent(): string {
|
||||
getCurrentTextContent(): string {
|
||||
return this._textContent;
|
||||
}
|
||||
getLatestTextContent(callback: (text: string) => void): void {
|
||||
asyncErrorOnPreparingPendingViewUpdate('Editor.getLatestTextContent()');
|
||||
if (this._pendingViewModel === null) {
|
||||
callback(this._textContent);
|
||||
return;
|
||||
}
|
||||
this._deferred.push(() => callback(this._textContent));
|
||||
}
|
||||
setRootElement(nextRootElement: null | HTMLElement): void {
|
||||
const prevRootElement = this._rootElement;
|
||||
if (nextRootElement !== prevRootElement) {
|
||||
@ -478,7 +504,8 @@ declare export class OutlineEditor {
|
||||
getDecorators(): {[NodeKey]: ReactNode};
|
||||
getRootElement(): null | HTMLElement;
|
||||
setRootElement(rootElement: null | HTMLElement): void;
|
||||
getTextContent(): string;
|
||||
getCurrentTextContent(): string;
|
||||
getLatestTextContent((text: string) => void): () => void;
|
||||
getElementByKey(key: NodeKey): null | HTMLElement;
|
||||
getViewModel(): ViewModel;
|
||||
setViewModel(viewModel: ViewModel): void;
|
||||
|
@ -46,5 +46,6 @@
|
||||
"44": "reconcileNode: prevNode or nextNode does not exist in nodeMap",
|
||||
"45": "reconcileNode: parentDOM is null",
|
||||
"46": "Reconciliation: could not find DOM element for node key \"${key}\"",
|
||||
"47": "clearEditor expected plain text root first child to be a ParagraphNode"
|
||||
"47": "clearEditor expected plain text root first child to be a ParagraphNode",
|
||||
"48": "Editor.getLatestTextContent() can be asynchronous and cannot be used within Editor.update()"
|
||||
}
|
||||
|
Reference in New Issue
Block a user