@lexical/react
Version:
This package provides Lexical components and hooks for React applications.
160 lines (149 loc) • 6 kB
JavaScript
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
import { useCollaborationContext } from '@lexical/react/LexicalCollaborationContext';
import { LexicalComposerContext, createLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { useRef, useContext, useMemo, useEffect } from 'react';
import { jsx } from 'react/jsx-runtime';
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
// Do not require this module directly! Use normal `invariant` calls.
function formatDevErrorMessage(message) {
throw new Error(message);
}
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
/*@__INLINE__*/
function warnOnlyOnce(message) {
{
let run = false;
return () => {
if (!run) {
console.warn(message);
}
run = true;
};
}
}
function getTransformSetFromKlass(klass) {
const transform = klass.transform();
return new Set(transform ? [transform] : []);
}
const initialNodesWarning = warnOnlyOnce(`LexicalNestedComposer initialNodes is deprecated and will be removed in v0.32.0, it has never worked correctly.\nYou can configure your editor's nodes with createEditor({nodes: [], parentEditor: $getEditor()})`);
const explicitNamespaceWarning = warnOnlyOnce(`LexicalNestedComposer initialEditor should explicitly initialize its namespace when the node configuration differs from the parentEditor. For backwards compatibility, the namespace will be initialized from parentEditor until v0.32.0, but this has always had incorrect copy/paste behavior when the configuration differed.\nYou can configure your editor's namespace with createEditor({namespace: 'nested-editor-namespace', nodes: [], parentEditor: $getEditor()}).`);
function LexicalNestedComposer({
initialEditor,
children,
initialNodes,
initialTheme,
skipCollabChecks,
skipEditableListener
}) {
const wasCollabPreviouslyReadyRef = useRef(false);
const parentContext = useContext(LexicalComposerContext);
if (parentContext == null) {
{
formatDevErrorMessage(`Unexpected parent context null on a nested composer`);
}
}
const [parentEditor, {
getTheme: getParentTheme
}] = parentContext;
const composerContext = useMemo(() => {
const composerTheme = initialTheme || getParentTheme() || undefined;
const context = createLexicalComposerContext(parentContext, composerTheme);
if (composerTheme !== undefined) {
initialEditor._config.theme = composerTheme;
}
initialEditor._parentEditor = initialEditor._parentEditor || parentEditor;
const createEditorArgs = initialEditor._createEditorArgs;
const explicitNamespace = createEditorArgs && createEditorArgs.namespace;
if (!initialNodes) {
if (!(createEditorArgs && createEditorArgs.nodes)) {
const parentNodes = initialEditor._nodes = new Map(parentEditor._nodes);
if (!explicitNamespace) {
// This is the only safe situation to inherit the parent's namespace
initialEditor._config.namespace = parentEditor._config.namespace;
}
for (const [type, entry] of parentNodes) {
initialEditor._nodes.set(type, {
exportDOM: entry.exportDOM,
klass: entry.klass,
replace: entry.replace,
replaceWithKlass: entry.replaceWithKlass,
transforms: getTransformSetFromKlass(entry.klass)
});
}
} else if (!explicitNamespace) {
explicitNamespaceWarning();
initialEditor._config.namespace = parentEditor._config.namespace;
}
} else {
initialNodesWarning();
if (!explicitNamespace) {
explicitNamespaceWarning();
initialEditor._config.namespace = parentEditor._config.namespace;
}
for (let klass of initialNodes) {
let replace = null;
let replaceWithKlass = null;
if (typeof klass !== 'function') {
const options = klass;
klass = options.replace;
replace = options.with;
replaceWithKlass = options.withKlass || null;
}
const registeredKlass = initialEditor._nodes.get(klass.getType());
initialEditor._nodes.set(klass.getType(), {
exportDOM: registeredKlass ? registeredKlass.exportDOM : undefined,
klass,
replace,
replaceWithKlass,
transforms: getTransformSetFromKlass(klass)
});
}
}
return [initialEditor, context];
},
// We only do this for init
// eslint-disable-next-line react-hooks/exhaustive-deps
[]);
// If collaboration is enabled, make sure we don't render the children until the collaboration subdocument is ready.
const {
isCollabActive,
yjsDocMap
} = useCollaborationContext();
const isCollabReady = skipCollabChecks || wasCollabPreviouslyReadyRef.current || yjsDocMap.has(initialEditor.getKey());
useEffect(() => {
if (isCollabReady) {
wasCollabPreviouslyReadyRef.current = true;
}
}, [isCollabReady]);
// Update `isEditable` state of nested editor in response to the same change on parent editor.
useEffect(() => {
if (!skipEditableListener) {
const editableListener = editable => initialEditor.setEditable(editable);
editableListener(parentEditor.isEditable());
return parentEditor.registerEditableListener(editableListener);
}
}, [initialEditor, parentEditor, skipEditableListener]);
return /*#__PURE__*/jsx(LexicalComposerContext.Provider, {
value: composerContext,
children: !isCollabActive || isCollabReady ? children : null
});
}
export { LexicalNestedComposer };