Preview: index.umd.js
Size: 63.05 KB
/proc/thread-self/root/home/byroehnu/.trash/node_modules11/@tiptap/react/dist/index.umd.js
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@tiptap/extension-bubble-menu'), require('react'), require('react-dom'), require('@tiptap/core'), require('@tiptap/extension-floating-menu')) :
typeof define === 'function' && define.amd ? define(['exports', '@tiptap/extension-bubble-menu', 'react', 'react-dom', '@tiptap/core', '@tiptap/extension-floating-menu'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["@tiptap/react"] = {}, global.extensionBubbleMenu, global.React, global.ReactDOM, global.core, global.extensionFloatingMenu));
})(this, (function (exports, extensionBubbleMenu, React, ReactDOM, core, extensionFloatingMenu) { 'use strict';
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var shim = {exports: {}};
var useSyncExternalStoreShim_production_min = {};
/**
* @license React
* use-sync-external-store-shim.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredUseSyncExternalStoreShim_production_min;
function requireUseSyncExternalStoreShim_production_min () {
if (hasRequiredUseSyncExternalStoreShim_production_min) return useSyncExternalStoreShim_production_min;
hasRequiredUseSyncExternalStoreShim_production_min = 1;
var e=React;function h(a,b){return a===b&&(0!==a||1/a===1/b)||a!==a&&b!==b}var k="function"===typeof Object.is?Object.is:h,l=e.useState,m=e.useEffect,n=e.useLayoutEffect,p=e.useDebugValue;function q(a,b){var d=b(),f=l({inst:{value:d,getSnapshot:b}}),c=f[0].inst,g=f[1];n(function(){c.value=d;c.getSnapshot=b;r(c)&&g({inst:c});},[a,d,b]);m(function(){r(c)&&g({inst:c});return a(function(){r(c)&&g({inst:c});})},[a]);p(d);return d}
function r(a){var b=a.getSnapshot;a=a.value;try{var d=b();return !k(a,d)}catch(f){return !0}}function t(a,b){return b()}var u="undefined"===typeof window||"undefined"===typeof window.document||"undefined"===typeof window.document.createElement?t:q;useSyncExternalStoreShim_production_min.useSyncExternalStore=void 0!==e.useSyncExternalStore?e.useSyncExternalStore:u;
return useSyncExternalStoreShim_production_min;
}
var useSyncExternalStoreShim_development = {};
/**
* @license React
* use-sync-external-store-shim.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredUseSyncExternalStoreShim_development;
function requireUseSyncExternalStoreShim_development () {
if (hasRequiredUseSyncExternalStoreShim_development) return useSyncExternalStoreShim_development;
hasRequiredUseSyncExternalStoreShim_development = 1;
if (process.env.NODE_ENV !== "production") {
(function() {
/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart ===
'function'
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
}
var React$1 = React;
var ReactSharedInternals = React$1.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
function error(format) {
{
{
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
printWarning('error', format, args);
}
}
}
function printWarning(level, format, args) {
// When changing this logic, you might want to also
// update consoleWithStackDev.www.js as well.
{
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
var stack = ReactDebugCurrentFrame.getStackAddendum();
if (stack !== '') {
format += '%s';
args = args.concat([stack]);
} // eslint-disable-next-line react-internal/safe-string-coercion
var argsWithFormat = args.map(function (item) {
return String(item);
}); // Careful: RN currently depends on this prefix
argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it
// breaks IE9: https://github.com/facebook/react/issues/13610
// eslint-disable-next-line react-internal/no-production-logging
Function.prototype.apply.call(console[level], console, argsWithFormat);
}
}
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
;
}
var objectIs = typeof Object.is === 'function' ? Object.is : is;
// dispatch for CommonJS interop named imports.
var useState = React$1.useState,
useEffect = React$1.useEffect,
useLayoutEffect = React$1.useLayoutEffect,
useDebugValue = React$1.useDebugValue;
var didWarnOld18Alpha = false;
var didWarnUncachedGetSnapshot = false; // Disclaimer: This shim breaks many of the rules of React, and only works
// because of a very particular set of implementation details and assumptions
// -- change any one of them and it will break. The most important assumption
// is that updates are always synchronous, because concurrent rendering is
// only available in versions of React that also have a built-in
// useSyncExternalStore API. And we only use this shim when the built-in API
// does not exist.
//
// Do not assume that the clever hacks used by this hook also work in general.
// The point of this shim is to replace the need for hacks by other libraries.
function useSyncExternalStore(subscribe, getSnapshot, // Note: The shim does not use getServerSnapshot, because pre-18 versions of
// React do not expose a way to check if we're hydrating. So users of the shim
// will need to track that themselves and return the correct value
// from `getSnapshot`.
getServerSnapshot) {
{
if (!didWarnOld18Alpha) {
if (React$1.startTransition !== undefined) {
didWarnOld18Alpha = true;
error('You are using an outdated, pre-release alpha of React 18 that ' + 'does not support useSyncExternalStore. The ' + 'use-sync-external-store shim will not work correctly. Upgrade ' + 'to a newer pre-release.');
}
}
} // Read the current snapshot from the store on every render. Again, this
// breaks the rules of React, and only works here because of specific
// implementation details, most importantly that updates are
// always synchronous.
var value = getSnapshot();
{
if (!didWarnUncachedGetSnapshot) {
var cachedValue = getSnapshot();
if (!objectIs(value, cachedValue)) {
error('The result of getSnapshot should be cached to avoid an infinite loop');
didWarnUncachedGetSnapshot = true;
}
}
} // Because updates are synchronous, we don't queue them. Instead we force a
// re-render whenever the subscribed state changes by updating an some
// arbitrary useState hook. Then, during render, we call getSnapshot to read
// the current value.
//
// Because we don't actually use the state returned by the useState hook, we
// can save a bit of memory by storing other stuff in that slot.
//
// To implement the early bailout, we need to track some things on a mutable
// object. Usually, we would put that in a useRef hook, but we can stash it in
// our useState hook instead.
//
// To force a re-render, we call forceUpdate({inst}). That works because the
// new object always fails an equality check.
var _useState = useState({
inst: {
value: value,
getSnapshot: getSnapshot
}
}),
inst = _useState[0].inst,
forceUpdate = _useState[1]; // Track the latest getSnapshot function with a ref. This needs to be updated
// in the layout phase so we can access it during the tearing check that
// happens on subscribe.
useLayoutEffect(function () {
inst.value = value;
inst.getSnapshot = getSnapshot; // Whenever getSnapshot or subscribe changes, we need to check in the
// commit phase if there was an interleaved mutation. In concurrent mode
// this can happen all the time, but even in synchronous mode, an earlier
// effect may have mutated the store.
if (checkIfSnapshotChanged(inst)) {
// Force a re-render.
forceUpdate({
inst: inst
});
}
}, [subscribe, value, getSnapshot]);
useEffect(function () {
// Check for changes right before subscribing. Subsequent changes will be
// detected in the subscription handler.
if (checkIfSnapshotChanged(inst)) {
// Force a re-render.
forceUpdate({
inst: inst
});
}
var handleStoreChange = function () {
// TODO: Because there is no cross-renderer API for batching updates, it's
// up to the consumer of this library to wrap their subscription event
// with unstable_batchedUpdates. Should we try to detect when this isn't
// the case and print a warning in development?
// The store changed. Check if the snapshot changed since the last time we
// read from the store.
if (checkIfSnapshotChanged(inst)) {
// Force a re-render.
forceUpdate({
inst: inst
});
}
}; // Subscribe to the store and return a clean-up function.
return subscribe(handleStoreChange);
}, [subscribe]);
useDebugValue(value);
return value;
}
function checkIfSnapshotChanged(inst) {
var latestGetSnapshot = inst.getSnapshot;
var prevValue = inst.value;
try {
var nextValue = latestGetSnapshot();
return !objectIs(prevValue, nextValue);
} catch (error) {
return true;
}
}
function useSyncExternalStore$1(subscribe, getSnapshot, getServerSnapshot) {
// Note: The shim does not use getServerSnapshot, because pre-18 versions of
// React do not expose a way to check if we're hydrating. So users of the shim
// will need to track that themselves and return the correct value
// from `getSnapshot`.
return getSnapshot();
}
var canUseDOM = !!(typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined');
var isServerEnvironment = !canUseDOM;
var shim = isServerEnvironment ? useSyncExternalStore$1 : useSyncExternalStore;
var useSyncExternalStore$2 = React$1.useSyncExternalStore !== undefined ? React$1.useSyncExternalStore : shim;
useSyncExternalStoreShim_development.useSyncExternalStore = useSyncExternalStore$2;
/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop ===
'function'
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error());
}
})();
}
return useSyncExternalStoreShim_development;
}
if (process.env.NODE_ENV === 'production') {
shim.exports = requireUseSyncExternalStoreShim_production_min();
} else {
shim.exports = requireUseSyncExternalStoreShim_development();
}
var shimExports = shim.exports;
const mergeRefs = (...refs) => {
return (node) => {
refs.forEach(ref => {
if (typeof ref === 'function') {
ref(node);
}
else if (ref) {
ref.current = node;
}
});
};
};
/**
* This component renders all of the editor's node views.
*/
const Portals = ({ contentComponent, }) => {
// For performance reasons, we render the node view portals on state changes only
const renderers = shimExports.useSyncExternalStore(contentComponent.subscribe, contentComponent.getSnapshot, contentComponent.getServerSnapshot);
// This allows us to directly render the portals without any additional wrapper
return (React.createElement(React.Fragment, null, Object.values(renderers)));
};
function getInstance() {
const subscribers = new Set();
let renderers = {};
return {
/**
* Subscribe to the editor instance's changes.
*/
subscribe(callback) {
subscribers.add(callback);
return () => {
subscribers.delete(callback);
};
},
getSnapshot() {
return renderers;
},
getServerSnapshot() {
return renderers;
},
/**
* Adds a new NodeView Renderer to the editor.
*/
setRenderer(id, renderer) {
renderers = {
...renderers,
[id]: ReactDOM.createPortal(renderer.reactElement, renderer.element, id),
};
subscribers.forEach(subscriber => subscriber());
},
/**
* Removes a NodeView Renderer from the editor.
*/
removeRenderer(id) {
const nextRenderers = { ...renderers };
delete nextRenderers[id];
renderers = nextRenderers;
subscribers.forEach(subscriber => subscriber());
},
};
}
class PureEditorContent extends React.Component {
constructor(props) {
var _a;
super(props);
this.editorContentRef = React.createRef();
this.initialized = false;
this.state = {
hasContentComponentInitialized: Boolean((_a = props.editor) === null || _a === void 0 ? void 0 : _a.contentComponent),
};
}
componentDidMount() {
this.init();
}
componentDidUpdate() {
this.init();
}
init() {
const editor = this.props.editor;
if (editor && !editor.isDestroyed && editor.options.element) {
if (editor.contentComponent) {
return;
}
const element = this.editorContentRef.current;
element.append(...editor.options.element.childNodes);
editor.setOptions({
element,
});
editor.contentComponent = getInstance();
// Has the content component been initialized?
if (!this.state.hasContentComponentInitialized) {
// Subscribe to the content component
this.unsubscribeToContentComponent = editor.contentComponent.subscribe(() => {
this.setState(prevState => {
if (!prevState.hasContentComponentInitialized) {
return {
hasContentComponentInitialized: true,
};
}
return prevState;
});
// Unsubscribe to previous content component
if (this.unsubscribeToContentComponent) {
this.unsubscribeToContentComponent();
}
});
}
editor.createNodeViews();
this.initialized = true;
}
}
componentWillUnmount() {
const editor = this.props.editor;
if (!editor) {
return;
}
this.initialized = false;
if (!editor.isDestroyed) {
editor.view.setProps({
nodeViews: {},
});
}
if (this.unsubscribeToContentComponent) {
this.unsubscribeToContentComponent();
}
editor.contentComponent = null;
if (!editor.options.element.firstChild) {
return;
}
const newElement = document.createElement('div');
newElement.append(...editor.options.element.childNodes);
editor.setOptions({
element: newElement,
});
}
render() {
const { editor, innerRef, ...rest } = this.props;
return (React.createElement(React.Fragment, null,
React.createElement("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }),
(editor === null || editor === void 0 ? void 0 : editor.contentComponent) && React.createElement(Portals, { contentComponent: editor.contentComponent })));
}
}
// EditorContent should be re-created whenever the Editor instance changes
const EditorContentWithKey = React.forwardRef((props, ref) => {
const key = React.useMemo(() => {
return Math.floor(Math.random() * 0xffffffff).toString();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.editor]);
// Can't use JSX here because it conflicts with the type definition of Vue's JSX, so use createElement
return React.createElement(PureEditorContent, {
key,
innerRef: ref,
...props,
});
});
const EditorContent = React.memo(EditorContentWithKey);
var react = function equal(a, b) {
if (a === b) return true;
if (a && b && typeof a == 'object' && typeof b == 'object') {
if (a.constructor !== b.constructor) return false;
var length, i, keys;
if (Array.isArray(a)) {
length = a.length;
if (length != b.length) return false;
for (i = length; i-- !== 0;)
if (!equal(a[i], b[i])) return false;
return true;
}
if ((a instanceof Map) && (b instanceof Map)) {
if (a.size !== b.size) return false;
for (i of a.entries())
if (!b.has(i[0])) return false;
for (i of a.entries())
if (!equal(i[1], b.get(i[0]))) return false;
return true;
}
if ((a instanceof Set) && (b instanceof Set)) {
if (a.size !== b.size) return false;
for (i of a.entries())
if (!b.has(i[0])) return false;
return true;
}
if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
length = a.length;
if (length != b.length) return false;
for (i = length; i-- !== 0;)
if (a[i] !== b[i]) return false;
return true;
}
if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags;
if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf();
if (a.toString !== Object.prototype.toString) return a.toString() === b.toString();
keys = Object.keys(a);
length = keys.length;
if (length !== Object.keys(b).length) return false;
for (i = length; i-- !== 0;)
if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false;
for (i = length; i-- !== 0;) {
var key = keys[i];
if (key === '_owner' && a.$$typeof) {
// React-specific: avoid traversing React elements' _owner.
// _owner contains circular references
// and is not needed when comparing the actual elements (and not their owners)
continue;
}
if (!equal(a[key], b[key])) return false;
}
return true;
}
// true if both NaN, false otherwise
return a!==a && b!==b;
};
var deepEqual = /*@__PURE__*/getDefaultExportFromCjs(react);
var withSelector = {exports: {}};
var withSelector_production_min = {};
/**
* @license React
* use-sync-external-store-shim/with-selector.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredWithSelector_production_min;
function requireWithSelector_production_min () {
if (hasRequiredWithSelector_production_min) return withSelector_production_min;
hasRequiredWithSelector_production_min = 1;
var h=React,n=shimExports;function p(a,b){return a===b&&(0!==a||1/a===1/b)||a!==a&&b!==b}var q="function"===typeof Object.is?Object.is:p,r=n.useSyncExternalStore,t=h.useRef,u=h.useEffect,v=h.useMemo,w=h.useDebugValue;
withSelector_production_min.useSyncExternalStoreWithSelector=function(a,b,e,l,g){var c=t(null);if(null===c.current){var f={hasValue:!1,value:null};c.current=f;}else f=c.current;c=v(function(){function a(a){if(!c){c=!0;d=a;a=l(a);if(void 0!==g&&f.hasValue){var b=f.value;if(g(b,a))return k=b}return k=a}b=k;if(q(d,a))return b;var e=l(a);if(void 0!==g&&g(b,e))return b;d=a;return k=e}var c=!1,d,k,m=void 0===e?null:e;return [function(){return a(b())},null===m?void 0:function(){return a(m())}]},[b,e,l,g]);var d=r(a,c[0],c[1]);
u(function(){f.hasValue=!0;f.value=d;},[d]);w(d);return d};
return withSelector_production_min;
}
var withSelector_development = {};
/**
* @license React
* use-sync-external-store-shim/with-selector.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredWithSelector_development;
function requireWithSelector_development () {
if (hasRequiredWithSelector_development) return withSelector_development;
hasRequiredWithSelector_development = 1;
if (process.env.NODE_ENV !== "production") {
(function() {
/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart ===
'function'
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
}
var React$1 = React;
var shim = shimExports;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
;
}
var objectIs = typeof Object.is === 'function' ? Object.is : is;
var useSyncExternalStore = shim.useSyncExternalStore;
// for CommonJS interop.
var useRef = React$1.useRef,
useEffect = React$1.useEffect,
useMemo = React$1.useMemo,
useDebugValue = React$1.useDebugValue; // Same as useSyncExternalStore, but supports selector and isEqual arguments.
function useSyncExternalStoreWithSelector(subscribe, getSnapshot, getServerSnapshot, selector, isEqual) {
// Use this to track the rendered snapshot.
var instRef = useRef(null);
var inst;
if (instRef.current === null) {
inst = {
hasValue: false,
value: null
};
instRef.current = inst;
} else {
inst = instRef.current;
}
var _useMemo = useMemo(function () {
// Track the memoized state using closure variables that are local to this
// memoized instance of a getSnapshot function. Intentionally not using a
// useRef hook, because that state would be shared across all concurrent
// copies of the hook/component.
var hasMemo = false;
var memoizedSnapshot;
var memoizedSelection;
var memoizedSelector = function (nextSnapshot) {
if (!hasMemo) {
// The first time the hook is called, there is no memoized result.
hasMemo = true;
memoizedSnapshot = nextSnapshot;
var _nextSelection = selector(nextSnapshot);
if (isEqual !== undefined) {
// Even if the selector has changed, the currently rendered selection
// may be equal to the new selection. We should attempt to reuse the
// current value if possible, to preserve downstream memoizations.
if (inst.hasValue) {
var currentSelection = inst.value;
if (isEqual(currentSelection, _nextSelection)) {
memoizedSelection = currentSelection;
return currentSelection;
}
}
}
memoizedSelection = _nextSelection;
return _nextSelection;
} // We may be able to reuse the previous invocation's result.
// We may be able to reuse the previous invocation's result.
var prevSnapshot = memoizedSnapshot;
var prevSelection = memoizedSelection;
if (objectIs(prevSnapshot, nextSnapshot)) {
// The snapshot is the same as last time. Reuse the previous selection.
return prevSelection;
} // The snapshot has changed, so we need to compute a new selection.
// The snapshot has changed, so we need to compute a new selection.
var nextSelection = selector(nextSnapshot); // If a custom isEqual function is provided, use that to check if the data
// has changed. If it hasn't, return the previous selection. That signals
// to React that the selections are conceptually equal, and we can bail
// out of rendering.
// If a custom isEqual function is provided, use that to check if the data
// has changed. If it hasn't, return the previous selection. That signals
// to React that the selections are conceptually equal, and we can bail
// out of rendering.
if (isEqual !== undefined && isEqual(prevSelection, nextSelection)) {
return prevSelection;
}
memoizedSnapshot = nextSnapshot;
memoizedSelection = nextSelection;
return nextSelection;
}; // Assigning this to a constant so that Flow knows it can't change.
// Assigning this to a constant so that Flow knows it can't change.
var maybeGetServerSnapshot = getServerSnapshot === undefined ? null : getServerSnapshot;
var getSnapshotWithSelector = function () {
return memoizedSelector(getSnapshot());
};
var getServerSnapshotWithSelector = maybeGetServerSnapshot === null ? undefined : function () {
return memoizedSelector(maybeGetServerSnapshot());
};
return [getSnapshotWithSelector, getServerSnapshotWithSelector];
}, [getSnapshot, getServerSnapshot, selector, isEqual]),
getSelection = _useMemo[0],
getServerSelection = _useMemo[1];
var value = useSyncExternalStore(subscribe, getSelection, getServerSelection);
useEffect(function () {
inst.hasValue = true;
inst.value = value;
}, [value]);
useDebugValue(value);
return value;
}
withSelector_development.useSyncExternalStoreWithSelector = useSyncExternalStoreWithSelector;
/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop ===
'function'
) {
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error());
}
})();
}
return withSelector_development;
}
if (process.env.NODE_ENV === 'production') {
withSelector.exports = requireWithSelector_production_min();
} else {
withSelector.exports = requireWithSelector_development();
}
var withSelectorExports = withSelector.exports;
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
/**
* To synchronize the editor instance with the component state,
* we need to create a separate instance that is not affected by the component re-renders.
*/
class EditorStateManager {
constructor(initialEditor) {
this.transactionNumber = 0;
this.lastTransactionNumber = 0;
this.subscribers = new Set();
this.editor = initialEditor;
this.lastSnapshot = { editor: initialEditor, transactionNumber: 0 };
this.getSnapshot = this.getSnapshot.bind(this);
this.getServerSnapshot = this.getServerSnapshot.bind(this);
this.watch = this.watch.bind(this);
this.subscribe = this.subscribe.bind(this);
}
/**
* Get the current editor instance.
*/
getSnapshot() {
if (this.transactionNumber === this.lastTransactionNumber) {
return this.lastSnapshot;
}
this.lastTransactionNumber = this.transactionNumber;
this.lastSnapshot = { editor: this.editor, transactionNumber: this.transactionNumber };
return this.lastSnapshot;
}
/**
* Always disable the editor on the server-side.
*/
getServerSnapshot() {
return { editor: null, transactionNumber: 0 };
}
/**
* Subscribe to the editor instance's changes.
*/
subscribe(callback) {
this.subscribers.add(callback);
return () => {
this.subscribers.delete(callback);
};
}
/**
* Watch the editor instance for changes.
*/
watch(nextEditor) {
this.editor = nextEditor;
if (this.editor) {
/**
* This will force a re-render when the editor state changes.
* This is to support things like `editor.can().toggleBold()` in components that `useEditor`.
* This could be more efficient, but it's a good trade-off for now.
*/
const fn = () => {
this.transactionNumber += 1;
this.subscribers.forEach(callback => callback());
};
const currentEditor = this.editor;
currentEditor.on('transaction', fn);
return () => {
currentEditor.off('transaction', fn);
};
}
return undefined;
}
}
/**
* This hook allows you to watch for changes on the editor instance.
* It will allow you to select a part of the editor state and re-render the component when it changes.
* @example
* ```tsx
* const editor = useEditor({...options})
* const { currentSelection } = useEditorState({
* editor,
* selector: snapshot => ({ currentSelection: snapshot.editor.state.selection }),
* })
*/
function useEditorState(options) {
var _a;
const [editorStateManager] = React.useState(() => new EditorStateManager(options.editor));
// Using the `useSyncExternalStore` hook to sync the editor instance with the component state
const selectedState = withSelectorExports.useSyncExternalStoreWithSelector(editorStateManager.subscribe, editorStateManager.getSnapshot, editorStateManager.getServerSnapshot, options.selector, (_a = options.equalityFn) !== null && _a !== void 0 ? _a : deepEqual);
useIsomorphicLayoutEffect(() => {
return editorStateManager.watch(options.editor);
}, [options.editor, editorStateManager]);
React.useDebugValue(selectedState);
return selectedState;
}
const isDev = process.env.NODE_ENV !== 'production';
const isSSR = typeof window === 'undefined';
const isNext = isSSR || Boolean(typeof window !== 'undefined' && window.next);
/**
* This class handles the creation, destruction, and re-creation of the editor instance.
*/
class EditorInstanceManager {
constructor(options) {
/**
* The current editor instance.
*/
this.editor = null;
/**
* The subscriptions to notify when the editor instance
* has been created or destroyed.
*/
this.subscriptions = new Set();
/**
* Whether the editor has been mounted.
*/
this.isComponentMounted = false;
/**
* The most recent dependencies array.
*/
this.previousDeps = null;
/**
* The unique instance ID. This is used to identify the editor instance. And will be re-generated for each new instance.
*/
this.instanceId = '';
this.options = options;
this.subscriptions = new Set();
this.setEditor(this.getInitialEditor());
this.scheduleDestroy();
this.getEditor = this.getEditor.bind(this);
this.getServerSnapshot = this.getServerSnapshot.bind(this);
this.subscribe = this.subscribe.bind(this);
this.refreshEditorInstance = this.refreshEditorInstance.bind(this);
this.scheduleDestroy = this.scheduleDestroy.bind(this);
this.onRender = this.onRender.bind(this);
this.createEditor = this.createEditor.bind(this);
}
setEditor(editor) {
this.editor = editor;
this.instanceId = Math.random().toString(36).slice(2, 9);
// Notify all subscribers that the editor instance has been created
this.subscriptions.forEach(cb => cb());
}
getInitialEditor() {
if (this.options.current.immediatelyRender === undefined) {
if (isSSR || isNext) {
// TODO in the next major release, we should throw an error here
if (isDev) {
/**
* Throw an error in development, to make sure the developer is aware that tiptap cannot be SSR'd
* and that they need to set `immediatelyRender` to `false` to avoid hydration mismatches.
*/
console.warn('Tiptap Error: SSR has been detected, please set `immediatelyRender` explicitly to `false` to avoid hydration mismatches.');
}
// Best faith effort in production, run the code in the legacy mode to avoid hydration mismatches and errors in production
return null;
}
// Default to immediately rendering when client-side rendering
return this.createEditor();
}
if (this.options.current.immediatelyRender && isSSR && isDev) {
// Warn in development, to make sure the developer is aware that tiptap cannot be SSR'd, set `immediatelyRender` to `false` to avoid hydration mismatches.
throw new Error('Tiptap Error: SSR has been detected, and `immediatelyRender` has been set to `true` this is an unsupported configuration that may result in errors, explicitly set `immediatelyRender` to `false` to avoid hydration mismatches.');
}
if (this.options.current.immediatelyRender) {
return this.createEditor();
}
return null;
}
/**
* Create a new editor instance. And attach event listeners.
*/
createEditor() {
const optionsToApply = {
...this.options.current,
// Always call the most recent version of the callback function by default
onBeforeCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBeforeCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onBlur: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onDestroy: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onFocus: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onSelectionUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onTransaction: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onContentError: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onDrop: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDrop) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
onPaste: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onPaste) === null || _b === void 0 ? void 0 : _b.call(_a, ...args); },
};
const editor = new core.Editor(optionsToApply);
// no need to keep track of the event listeners, they will be removed when the editor is destroyed
return editor;
}
/**
* Get the current editor instance.
*/
getEditor() {
return this.editor;
}
/**
* Always disable the editor on the server-side.
*/
getServerSnapshot() {
return null;
}
/**
* Subscribe to the editor instance's changes.
*/
subscribe(onStoreChange) {
this.subscriptions.add(onStoreChange);
return () => {
this.subscriptions.delete(onStoreChange);
};
}
static compareOptions(a, b) {
return Object.keys(a).every(key => {
if (['onCreate', 'onBeforeCreate', 'onDestroy', 'onUpdate', 'onTransaction', 'onFocus', 'onBlur', 'onSelectionUpdate', 'onContentError', 'onDrop', 'onPaste'].includes(key)) {
// we don't want to compare callbacks, they are always different and only registered once
return true;
}
// We often encourage putting extensions inlined in the options object, so we will do a slightly deeper comparison here
if (key === 'extensions' && a.extensions && b.extensions) {
if (a.extensions.length !== b.extensions.length) {
return false;
}
return a.extensions.every((extension, index) => {
var _a;
if (extension !== ((_a = b.extensions) === null || _a === void 0 ? void 0 : _a[index])) {
return false;
}
return true;
});
}
if (a[key] !== b[key]) {
// if any of the options have changed, we should update the editor options
return false;
}
return true;
});
}
/**
* On each render, we will create, update, or destroy the editor instance.
* @param deps The dependencies to watch for changes
* @returns A cleanup function
*/
onRender(deps) {
// The returned callback will run on each render
return () => {
this.isComponentMounted = true;
// Cleanup any scheduled destructions, since we are currently rendering
clearTimeout(this.scheduledDestructionTimeout);
if (this.editor && !this.editor.isDestroyed && deps.length === 0) {
// if the editor does exist & deps are empty, we don't need to re-initialize the editor generally
if (!EditorInstanceManager.compareOptions(this.options.current, this.editor.options)) {
// But, the options are different, so we need to update the editor options
// Still, this is faster than re-creating the editor
this.editor.setOptions({
...this.options.current,
editable: this.editor.isEditable,
});
}
}
else {
// When the editor:
// - does not yet exist
// - is destroyed
// - the deps array changes
// We need to destroy the editor instance and re-initialize it
this.refreshEditorInstance(deps);
}
return () => {
this.isComponentMounted = false;
this.scheduleDestroy();
};
};
}
/**
* Recreate the editor instance if the dependencies have changed.
*/
refreshEditorInstance(deps) {
if (this.editor && !this.editor.isDestroyed) {
// Editor instance already exists
if (this.previousDeps === null) {
// If lastDeps has not yet been initialized, reuse the current editor instance
this.previousDeps = deps;
return;
}
const depsAreEqual = this.previousDeps.length === deps.length
&& this.previousDeps.every((dep, index) => dep === deps[index]);
if (depsAreEqual) {
// deps exist and are equal, no need to recreate
return;
}
}
if (this.editor && !this.editor.isDestroyed) {
// Destroy the editor instance if it exists
this.editor.destroy();
}
this.setEditor(this.createEditor());
// Update the lastDeps to the current deps
this.previousDeps = deps;
}
/**
* Schedule the destruction of the editor instance.
* This will only destroy the editor if it was not mounted on the next tick.
* This is to avoid destroying the editor instance when it's actually still mounted.
*/
scheduleDestroy() {
const currentInstanceId = this.instanceId;
const currentEditor = this.editor;
// Wait two ticks to see if the component is still mounted
this.scheduledDestructionTimeout = setTimeout(() => {
if (this.isComponentMounted && this.instanceId === currentInstanceId) {
// If still mounted on the following tick, with the same instanceId, do not destroy the editor
if (currentEditor) {
// just re-apply options as they might have changed
currentEditor.setOptions(this.options.current);
}
return;
}
if (currentEditor && !currentEditor.isDestroyed) {
currentEditor.destroy();
if (this.instanceId === currentInstanceId) {
this.setEditor(null);
}
}
// This allows the effect to run again between ticks
// which may save us from having to re-create the editor
}, 1);
}
}
function useEditor(options = {}, deps = []) {
const mostRecentOptions = React.useRef(options);
mostRecentOptions.current = options;
const [instanceManager] = React.useState(() => new EditorInstanceManager(mostRecentOptions));
const editor = shimExports.useSyncExternalStore(instanceManager.subscribe, instanceManager.getEditor, instanceManager.getServerSnapshot);
React.useDebugValue(editor);
// This effect will handle creating/updating the editor instance
// eslint-disable-next-line react-hooks/exhaustive-deps
React.useEffect(instanceManager.onRender(deps));
// The default behavior is to re-render on each transaction
// This is legacy behavior that will be removed in future versions
useEditorState({
editor,
selector: ({ transactionNumber }) => {
if (options.shouldRerenderOnTransaction === false) {
// This will prevent the editor from re-rendering on each transaction
return null;
}
// This will avoid re-rendering on the first transaction when `immediatelyRender` is set to `true`
if (options.immediatelyRender && transactionNumber === 0) {
return 0;
}
return transactionNumber + 1;
},
});
return editor;
}
const EditorContext = React.createContext({
editor: null,
});
const EditorConsumer = EditorContext.Consumer;
/**
* A hook to get the current editor instance.
*/
const useCurrentEditor = () => React.useContext(EditorContext);
/**
* This is the provider component for the editor.
* It allows the editor to be accessible across the entire component tree
* with `useCurrentEditor`.
*/
function EditorProvider({ children, slotAfter, slotBefore, editorContainerProps = {}, ...editorOptions }) {
const editor = useEditor(editorOptions);
if (!editor) {
return null;
}
return (React.createElement(EditorContext.Provider, { value: { editor } },
slotBefore,
React.createElement(EditorConsumer, null, ({ editor: currentEditor }) => (React.createElement(EditorContent, { editor: currentEditor, ...editorContainerProps }))),
children,
slotAfter));
}
const BubbleMenu = (props) => {
const [element, setElement] = React.useState(null);
const { editor: currentEditor } = useCurrentEditor();
React.useEffect(() => {
var _a;
if (!element) {
return;
}
if (((_a = props.editor) === null || _a === void 0 ? void 0 : _a.isDestroyed) || (currentEditor === null || currentEditor === void 0 ? void 0 : currentEditor.isDestroyed)) {
return;
}
const { pluginKey = 'bubbleMenu', editor, tippyOptions = {}, updateDelay, shouldShow = null, } = props;
const menuEditor = editor || currentEditor;
if (!menuEditor) {
console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.');
return;
}
const plugin = extensionBubbleMenu.BubbleMenuPlugin({
updateDelay,
editor: menuEditor,
element,
pluginKey,
shouldShow,
tippyOptions,
});
menuEditor.registerPlugin(plugin);
return () => { menuEditor.unregisterPlugin(pluginKey); };
}, [props.editor, currentEditor, element]);
return (React.createElement("div", { ref: setElement, className: props.className, style: { visibility: 'hidden' } }, props.children));
};
const FloatingMenu = (props) => {
const [element, setElement] = React.useState(null);
const { editor: currentEditor } = useCurrentEditor();
React.useEffect(() => {
var _a;
if (!element) {
return;
}
if (((_a = props.editor) === null || _a === void 0 ? void 0 : _a.isDestroyed) || (currentEditor === null || currentEditor === void 0 ? void 0 : currentEditor.isDestroyed)) {
return;
}
const { pluginKey = 'floatingMenu', editor, tippyOptions = {}, shouldShow = null, } = props;
const menuEditor = editor || currentEditor;
if (!menuEditor) {
console.warn('FloatingMenu component is not rendered inside of an editor component or does not have editor prop.');
return;
}
const plugin = extensionFloatingMenu.FloatingMenuPlugin({
pluginKey,
editor: menuEditor,
element,
tippyOptions,
shouldShow,
});
menuEditor.registerPlugin(plugin);
return () => { menuEditor.unregisterPlugin(pluginKey); };
}, [
props.editor,
currentEditor,
element,
]);
return (React.createElement("div", { ref: setElement, className: props.className, style: { visibility: 'hidden' } }, props.children));
};
const ReactNodeViewContext = React.createContext({
onDragStart: undefined,
});
const useReactNodeView = () => React.useContext(ReactNodeViewContext);
const NodeViewContent = props => {
const Tag = props.as || 'div';
const { nodeViewContentRef } = useReactNodeView();
return (
// @ts-ignore
React.createElement(Tag, { ...props, ref: nodeViewContentRef, "data-node-view-content": "", style: {
whiteSpace: 'pre-wrap',
...props.style,
} }));
};
const NodeViewWrapper = React.forwardRef((props, ref) => {
const { onDragStart } = useReactNodeView();
const Tag = props.as || 'div';
return (
// @ts-ignore
React.createElement(Tag, { ...props, ref: ref, "data-node-view-wrapper": "", onDragStart: onDragStart, style: {
whiteSpace: 'normal',
...props.style,
} }));
});
/**
* Check if a component is a class component.
* @param Component
* @returns {boolean}
*/
function isClassComponent(Component) {
return !!(typeof Component === 'function'
&& Component.prototype
&& Component.prototype.isReactComponent);
}
/**
* Check if a component is a forward ref component.
* @param Component
* @returns {boolean}
*/
function isForwardRefComponent(Component) {
return !!(typeof Component === 'object'
&& Component.$$typeof
&& (Component.$$typeof.toString() === 'Symbol(react.forward_ref)'
|| Component.$$typeof.description === 'react.forward_ref'));
}
/**
* Check if a component is a memoized component.
* @param Component
* @returns {boolean}
*/
function isMemoComponent(Component) {
return !!(typeof Component === 'object'
&& Component.$$typeof
&& (Component.$$typeof.toString() === 'Symbol(react.memo)' || Component.$$typeof.description === 'react.memo'));
}
/**
* Check if a component can safely receive a ref prop.
* This includes class components, forwardRef components, and memoized components
* that wrap forwardRef or class components.
* @param Component
* @returns {boolean}
*/
function canReceiveRef(Component) {
// Check if it's a class component
if (isClassComponent(Component)) {
return true;
}
// Check if it's a forwardRef component
if (isForwardRefComponent(Component)) {
return true;
}
// Check if it's a memoized component
if (isMemoComponent(Component)) {
// For memoized components, check the wrapped component
const wrappedComponent = Component.type;
if (wrappedComponent) {
return isClassComponent(wrappedComponent) || isForwardRefComponent(wrappedComponent);
}
}
return false;
}
/**
* Check if we're running React 19+ by detecting if function components support ref props
* @returns {boolean}
*/
function isReact19Plus() {
// React 19 is detected by checking React version if available
// In practice, we'll use a more conservative approach and assume React 18 behavior
// unless we can definitively detect React 19
try {
// @ts-ignore
if (React.version) {
const majorVersion = parseInt(React.version.split('.')[0], 10);
return majorVersion >= 19;
}
}
catch {
// Fallback to React 18 behavior if we can't determine version
}
return false;
}
/**
* The ReactRenderer class. It's responsible for rendering React components inside the editor.
* @example
* new ReactRenderer(MyComponent, {
* editor,
* props: {
* foo: 'bar',
* },
* as: 'span',
* })
*/
class ReactRenderer {
/**
* Immediately creates element and renders the provided React component.
*/
constructor(component, { editor, props = {}, as = 'div', className = '', }) {
this.ref = null;
this.id = Math.floor(Math.random() * 0xFFFFFFFF).toString();
this.component = component;
this.editor = editor;
this.props = props;
this.element = document.createElement(as);
this.element.classList.add('react-renderer');
if (className) {
this.element.classList.add(...className.split(' '));
}
// If the editor is already initialized, we will need to
// synchronously render the component to ensure it renders
// together with Prosemirror's rendering.
if (this.editor.isInitialized) {
ReactDOM.flushSync(() => {
this.render();
});
}
else {
queueMicrotask(() => {
this.render();
});
}
}
/**
* Render the React component.
*/
render() {
var _a;
const Component = this.component;
const props = this.props;
const editor = this.editor;
// Handle ref forwarding with React 18/19 compatibility
const isReact19 = isReact19Plus();
const componentCanReceiveRef = canReceiveRef(Component);
const elementProps = { ...props };
// Always remove ref if the component cannot receive it (unless React 19+)
if (elementProps.ref && !(isReact19 || componentCanReceiveRef)) {
delete elementProps.ref;
}
// Only assign our own ref if allowed
if (!elementProps.ref && (isReact19 || componentCanReceiveRef)) {
// @ts-ignore - Setting ref prop for compatible components
elementProps.ref = (ref) => {
this.ref = ref;
};
}
this.reactElement = React.createElement(Component, { ...elementProps });
(_a = editor === null || editor === void 0 ? void 0 : editor.contentComponent) === null || _a === void 0 ? void 0 : _a.setRenderer(this.id, this);
}
/**
* Re-renders the React component with new props.
*/
updateProps(props = {}) {
this.props = {
...this.props,
...props,
};
this.render();
}
/**
* Destroy the React component.
*/
destroy() {
var _a;
const editor = this.editor;
(_a = editor === null || editor === void 0 ? void 0 : editor.contentComponent) === null || _a === void 0 ? void 0 : _a.removeRenderer(this.id);
}
/**
* Update the attributes of the element that holds the React component.
*/
updateAttributes(attributes) {
Object.keys(attributes).forEach(key => {
this.element.setAttribute(key, attributes[key]);
});
}
}
class ReactNodeView extends core.NodeView {
constructor(component, props, options) {
super(component, props, options);
if (!this.node.isLeaf) {
if (this.options.contentDOMElementTag) {
this.contentDOMElement = document.createElement(this.options.contentDOMElementTag);
}
else {
this.contentDOMElement = document.createElement(this.node.isInline ? 'span' : 'div');
}
this.contentDOMElement.dataset.nodeViewContentReact = '';
this.contentDOMElement.dataset.nodeViewWrapper = '';
// For some reason the whiteSpace prop is not inherited properly in Chrome and Safari
// With this fix it seems to work fine
// See: https://github.com/ueberdosis/tiptap/issues/1197
this.contentDOMElement.style.whiteSpace = 'inherit';
const contentTarget = this.dom.querySelector('[data-node-view-content]');
if (!contentTarget) {
return;
}
contentTarget.appendChild(this.contentDOMElement);
}
}
/**
* Setup the React component.
* Called on initialization.
*/
mount() {
const props = {
editor: this.editor,
node: this.node,
decorations: this.decorations,
innerDecorations: this.innerDecorations,
view: this.view,
selected: false,
extension: this.extension,
HTMLAttributes: this.HTMLAttributes,
getPos: () => this.getPos(),
updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
deleteNode: () => this.deleteNode(),
ref: React.createRef(),
};
if (!this.component.displayName) {
const capitalizeFirstChar = (string) => {
return string.charAt(0).toUpperCase() + string.substring(1);
};
this.component.displayName = capitalizeFirstChar(this.extension.name);
}
const onDragStart = this.onDragStart.bind(this);
const nodeViewContentRef = element => {
if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
// remove the nodeViewWrapper attribute from the element
if (element.hasAttribute('data-node-view-wrapper')) {
element.removeAttribute('data-node-view-wrapper');
}
element.appendChild(this.contentDOMElement);
}
};
const context = { onDragStart, nodeViewContentRef };
const Component = this.component;
// For performance reasons, we memoize the provider component
// And all of the things it requires are declared outside of the component, so it doesn't need to re-render
const ReactNodeViewProvider = React.memo(componentProps => {
return (React.createElement(ReactNodeViewContext.Provider, { value: context }, React.createElement(Component, componentProps)));
});
ReactNodeViewProvider.displayName = 'ReactNodeView';
let as = this.node.isInline ? 'span' : 'div';
if (this.options.as) {
as = this.options.as;
}
const { className = '' } = this.options;
this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this);
this.renderer = new ReactRenderer(ReactNodeViewProvider, {
editor: this.editor,
props,
as,
className: `node-${this.node.type.name} ${className}`.trim(),
});
this.editor.on('selectionUpdate', this.handleSelectionUpdate);
this.updateElementAttributes();
}
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom() {
var _a;
if (this.renderer.element.firstElementChild
&& !((_a = this.renderer.element.firstElementChild) === null || _a === void 0 ? void 0 : _a.hasAttribute('data-node-view-wrapper'))) {
throw Error('Please use the NodeViewWrapper component for your node view.');
}
return this.renderer.element;
}
/**
* Return the content DOM element.
* This is the element that will be used to display the rich-text content of the node.
*/
get contentDOM() {
if (this.node.isLeaf) {
return null;
}
return this.contentDOMElement;
}
/**
* On editor selection update, check if the node is selected.
* If it is, call `selectNode`, otherwise call `deselectNode`.
*/
handleSelectionUpdate() {
const { from, to } = this.editor.state.selection;
const pos = this.getPos();
if (typeof pos !== 'number') {
return;
}
if (from <= pos && to >= pos + this.node.nodeSize) {
if (this.renderer.props.selected) {
return;
}
this.selectNode();
}
else {
if (!this.renderer.props.selected) {
return;
}
this.deselectNode();
}
}
/**
* On update, update the React component.
* To prevent unnecessary updates, the `update` option can be used.
*/
update(node, decorations, innerDecorations) {
const rerenderComponent = (props) => {
this.renderer.updateProps(props);
if (typeof this.options.attrs === 'function') {
this.updateElementAttributes();
}
};
if (node.type !== this.node.type) {
return false;
}
if (typeof this.options.update === 'function') {
const oldNode = this.node;
const oldDecorations = this.decorations;
const oldInnerDecorations = this.innerDecorations;
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
return this.options.update({
oldNode,
oldDecorations,
newNode: node,
newDecorations: decorations,
oldInnerDecorations,
innerDecorations,
updateProps: () => rerenderComponent({ node, decorations, innerDecorations }),
});
}
if (node === this.node
&& this.decorations === decorations
&& this.innerDecorations === innerDecorations) {
return true;
}
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
rerenderComponent({ node, decorations, innerDecorations });
return true;
}
/**
* Select the node.
* Add the `selected` prop and the `ProseMirror-selectednode` class.
*/
selectNode() {
this.renderer.updateProps({
selected: true,
});
this.renderer.element.classList.add('ProseMirror-selectednode');
}
/**
* Deselect the node.
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
*/
deselectNode() {
this.renderer.updateProps({
selected: false,
});
this.renderer.element.classList.remove('ProseMirror-selectednode');
}
/**
* Destroy the React component instance.
*/
destroy() {
this.renderer.destroy();
this.editor.off('selectionUpdate', this.handleSelectionUpdate);
this.contentDOMElement = null;
}
/**
* Update the attributes of the top-level element that holds the React component.
* Applying the attributes defined in the `attrs` option.
*/
updateElementAttributes() {
if (this.options.attrs) {
let attrsObj = {};
if (typeof this.options.attrs === 'function') {
const extensionAttributes = this.editor.extensionManager.attributes;
const HTMLAttributes = core.getRenderedAttributes(this.node, extensionAttributes);
attrsObj = this.options.attrs({ node: this.node, HTMLAttributes });
}
else {
attrsObj = this.options.attrs;
}
this.renderer.updateAttributes(attrsObj);
}
}
}
/**
* Create a React node view renderer.
*/
function ReactNodeViewRenderer(component, options) {
return props => {
// try to get the parent component
// this is important for vue devtools to show the component hierarchy correctly
// maybe it’s `undefined` because <editor-content> isn’t rendered yet
if (!props.editor.contentComponent) {
return {};
}
return new ReactNodeView(component, props, options);
};
}
exports.BubbleMenu = BubbleMenu;
exports.EditorConsumer = EditorConsumer;
exports.EditorContent = EditorContent;
exports.EditorContext = EditorContext;
exports.EditorProvider = EditorProvider;
exports.FloatingMenu = FloatingMenu;
exports.NodeViewContent = NodeViewContent;
exports.NodeViewWrapper = NodeViewWrapper;
exports.PureEditorContent = PureEditorContent;
exports.ReactNodeView = ReactNodeView;
exports.ReactNodeViewContext = ReactNodeViewContext;
exports.ReactNodeViewRenderer = ReactNodeViewRenderer;
exports.ReactRenderer = ReactRenderer;
exports.useCurrentEditor = useCurrentEditor;
exports.useEditor = useEditor;
exports.useEditorState = useEditorState;
exports.useReactNodeView = useReactNodeView;
Object.keys(core).forEach(function (k) {
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return core[k]; }
});
});
}));
//# sourceMappingURL=index.umd.js.map
Directory Contents
Dirs: 0 × Files: 34