@lexical/react
Version:
This package provides Lexical components and hooks for React applications.
190 lines (181 loc) • 6.38 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.
*
*/
;
var extension = require('@lexical/extension');
var ReactExtension = require('@lexical/react/ReactExtension');
var ReactProviderExtension = require('@lexical/react/ReactProviderExtension');
var utils = require('@lexical/utils');
var lexical = require('lexical');
var react = require('react');
var reactDom = require('react-dom');
var client = require('react-dom/client');
var jsxRuntime = require('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);
}
function mountReactExtensionComponent(editor, opts) {
const {
props,
extension: extension$1,
...rest
} = opts;
const {
Component
} = extension.getExtensionDependencyFromEditor(editor, extension$1).output;
const element = props ? /*#__PURE__*/jsxRuntime.jsx(Component, {
...props
}) : null;
mountReactPluginElement(editor, {
...rest,
element
});
}
function mountReactPluginComponent(editor, opts) {
const {
Component,
props,
...rest
} = opts;
mountReactPluginElement(editor, {
...rest,
element: props ? /*#__PURE__*/jsxRuntime.jsx(Component, {
...props
}) : null
});
}
function mountReactPluginElement(editor, opts) {
extension.getExtensionDependencyFromEditor(editor, ReactPluginHostExtension).output.mountReactPlugin(opts);
}
function mountReactPluginHost(editor, container) {
extension.getExtensionDependencyFromEditor(editor, ReactPluginHostExtension).output.mountReactPluginHost(container);
}
const REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND = lexical.createCommand('REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND');
const REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND = lexical.createCommand('REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND');
function PluginHostDecorator({
context: [editor]
}) {
const {
mountedPluginsStore
} = extension.getExtensionDependencyFromEditor(editor, ReactPluginHostExtension).output;
const {
ErrorBoundary
} = extension.getExtensionDependencyFromEditor(editor, ReactExtension.ReactExtension).config;
const onError = editor._onError.bind(editor);
const [{
plugins
}, setMountedPlugins] = react.useState(() => mountedPluginsStore.peek());
react.useEffect(() => extension.effect(() => setMountedPlugins(mountedPluginsStore.value)), [mountedPluginsStore]);
const children = [];
for (const {
key,
element,
domNode
} of plugins.values()) {
if (!element) {
continue;
}
const wrapped = /*#__PURE__*/jsxRuntime.jsx(ErrorBoundary, {
onError: onError,
children: /*#__PURE__*/jsxRuntime.jsx(react.Suspense, {
fallback: null,
children: element
})
}, key);
children.push(domNode ? /*#__PURE__*/reactDom.createPortal(wrapped, domNode, key) : wrapped);
}
return children.length > 0 ? /*#__PURE__*/jsxRuntime.jsx(jsxRuntime.Fragment, {
children: children
}) : null;
}
/**
* This extension provides a React host for editors that are not built
* with LexicalExtensionComposer (e.g. you are using Vanilla JS or some
* other framework).
*
* You must use {@link mountReactPluginHost} for any React content to work.
* Afterwards, you may use {@link mountReactExtensionComponent} to
* render UI for a specific React Extension.
* {@link mountReactPluginComponent} and
* {@link mountReactPluginElement} can be used to render
* legacy React plug-ins (or any React content).
*/
const ReactPluginHostExtension = lexical.defineExtension({
build(editor, config, state) {
const mountedPluginsStore = extension.signal({
plugins: new Map()
});
return {
mountReactPlugin: arg => {
editor.dispatchCommand(REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND, arg);
},
// Using outputs to wrap commands will give us better error messages
// if the mount functions are called on an editor without this extension
mountReactPluginHost: container => editor.dispatchCommand(REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND, {
root: client.createRoot(container)
}),
mountedPluginsStore
};
},
dependencies: [ReactProviderExtension.ReactProviderExtension, lexical.configExtension(ReactExtension.ReactExtension, {
decorators: [PluginHostDecorator]
})],
name: '@lexical/react/ReactPluginHost',
register(editor, _config, state) {
let root;
const {
mountedPluginsStore
} = state.getOutput();
const {
Component
} = state.getDependency(ReactExtension.ReactExtension).output;
return utils.mergeRegister(() => {
if (root) {
root.unmount();
}
extension.untracked(() => {
mountedPluginsStore.value.plugins.clear();
});
}, editor.registerCommand(REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND, arg => {
// This runs before the PluginHost version
extension.untracked(() => {
const {
plugins
} = mountedPluginsStore.value;
plugins.set(arg.key, arg);
mountedPluginsStore.value = {
plugins
};
});
return false;
}, lexical.COMMAND_PRIORITY_CRITICAL), editor.registerCommand(REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND, arg => {
if (!(root === undefined)) {
formatDevErrorMessage(`ReactPluginHostExtension: Root is already mounted`);
}
root = arg.root;
root.render(/*#__PURE__*/jsxRuntime.jsx(Component, {
contentEditable: null
}));
return true;
}, lexical.COMMAND_PRIORITY_EDITOR));
}
});
exports.REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND = REACT_PLUGIN_HOST_MOUNT_PLUGIN_COMMAND;
exports.REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND = REACT_PLUGIN_HOST_MOUNT_ROOT_COMMAND;
exports.ReactPluginHostExtension = ReactPluginHostExtension;
exports.mountReactExtensionComponent = mountReactExtensionComponent;
exports.mountReactPluginComponent = mountReactPluginComponent;
exports.mountReactPluginElement = mountReactPluginElement;
exports.mountReactPluginHost = mountReactPluginHost;