// src/Context.tsx import { createContext, useContext, useMemo } from "react"; // src/EditorContent.tsx import React, { forwardRef } from "react"; import ReactDOM from "react-dom"; import { useSyncExternalStore } from "use-sync-external-store/shim/index.js"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var mergeRefs = (...refs) => { return (node) => { refs.forEach((ref) => { if (typeof ref === "function") { ref(node); } else if (ref) { ; ref.current = node; } }); }; }; var Portals = ({ contentComponent }) => { const renderers = useSyncExternalStore( contentComponent.subscribe, contentComponent.getSnapshot, contentComponent.getServerSnapshot ); return /* @__PURE__ */ jsx(Fragment, { children: Object.values(renderers) }); }; function getInstance() { const subscribers = /* @__PURE__ */ 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()); } }; } var PureEditorContent = class extends React.Component { constructor(props) { var _a; super(props); this.editorContentRef = React.createRef(); this.initialized = false; this.state = { hasContentComponentInitialized: Boolean((_a = props.editor) == null ? void 0 : _a.contentComponent) }; } componentDidMount() { this.init(); } componentDidUpdate() { this.init(); } init() { var _a; const editor = this.props.editor; if (editor && !editor.isDestroyed && ((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) { if (editor.contentComponent) { return; } const element = this.editorContentRef.current; element.append(...editor.view.dom.parentNode.childNodes); editor.setOptions({ element }); editor.contentComponent = getInstance(); if (!this.state.hasContentComponentInitialized) { this.unsubscribeToContentComponent = editor.contentComponent.subscribe(() => { this.setState((prevState) => { if (!prevState.hasContentComponentInitialized) { return { hasContentComponentInitialized: true }; } return prevState; }); if (this.unsubscribeToContentComponent) { this.unsubscribeToContentComponent(); } }); } editor.createNodeViews(); this.initialized = true; } } componentWillUnmount() { var _a; 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; try { if (!((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) { return; } const newElement = document.createElement("div"); newElement.append(...editor.view.dom.parentNode.childNodes); editor.setOptions({ element: newElement }); } catch { } } render() { const { editor, innerRef, ...rest } = this.props; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }), (editor == null ? void 0 : editor.contentComponent) && /* @__PURE__ */ jsx(Portals, { contentComponent: editor.contentComponent }) ] }); } }; var EditorContentWithKey = forwardRef( (props, ref) => { const key = React.useMemo(() => { return Math.floor(Math.random() * 4294967295).toString(); }, [props.editor]); return React.createElement(PureEditorContent, { key, innerRef: ref, ...props }); } ); var EditorContent = React.memo(EditorContentWithKey); // src/useEditor.ts import { Editor } from "@tiptap/core"; import { useDebugValue as useDebugValue2, useEffect as useEffect2, useRef, useState as useState2 } from "react"; import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js"; // src/useEditorState.ts import { deepEqual } from "fast-equals"; import { useDebugValue, useEffect, useLayoutEffect, useState } from "react"; import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js"; var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect; var EditorStateManager = class { constructor(initialEditor) { this.transactionNumber = 0; this.lastTransactionNumber = 0; this.subscribers = /* @__PURE__ */ 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) { const fn = () => { this.transactionNumber += 1; this.subscribers.forEach((callback) => callback()); }; const currentEditor = this.editor; currentEditor.on("transaction", fn); return () => { currentEditor.off("transaction", fn); }; } return void 0; } }; function useEditorState(options) { var _a; const [editorStateManager] = useState(() => new EditorStateManager(options.editor)); const selectedState = useSyncExternalStoreWithSelector( editorStateManager.subscribe, editorStateManager.getSnapshot, editorStateManager.getServerSnapshot, options.selector, (_a = options.equalityFn) != null ? _a : deepEqual ); useIsomorphicLayoutEffect(() => { return editorStateManager.watch(options.editor); }, [options.editor, editorStateManager]); useDebugValue(selectedState); return selectedState; } // src/useEditor.ts var isDev = process.env.NODE_ENV !== "production"; var isSSR = typeof window === "undefined"; var isNext = isSSR || Boolean(typeof window !== "undefined" && window.next); var EditorInstanceManager = 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 = /* @__PURE__ */ 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 = /* @__PURE__ */ 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); this.subscriptions.forEach((cb) => cb()); } getInitialEditor() { if (this.options.current.immediatelyRender === void 0) { if (isSSR || isNext) { if (isDev) { throw new Error( "Tiptap Error: SSR has been detected, please set `immediatelyRender` explicitly to `false` to avoid hydration mismatches." ); } return null; } return this.createEditor(); } if (this.options.current.immediatelyRender && isSSR && isDev) { 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 ? void 0 : _b.call(_a, ...args); }, onBlur: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) == null ? void 0 : _b.call(_a, ...args); }, onCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) == null ? void 0 : _b.call(_a, ...args); }, onDestroy: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) == null ? void 0 : _b.call(_a, ...args); }, onFocus: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) == null ? void 0 : _b.call(_a, ...args); }, onSelectionUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) == null ? void 0 : _b.call(_a, ...args); }, onTransaction: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) == null ? void 0 : _b.call(_a, ...args); }, onUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) == null ? void 0 : _b.call(_a, ...args); }, onContentError: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) == null ? void 0 : _b.call(_a, ...args); }, onDrop: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDrop) == null ? void 0 : _b.call(_a, ...args); }, onPaste: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onPaste) == null ? void 0 : _b.call(_a, ...args); }, onDelete: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDelete) == null ? void 0 : _b.call(_a, ...args); } }; const editor = new Editor(optionsToApply); 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)) { return true; } 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 ? void 0 : _a[index])) { return false; } return true; }); } if (a[key] !== b[key]) { 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) { return () => { this.isComponentMounted = true; clearTimeout(this.scheduledDestructionTimeout); if (this.editor && !this.editor.isDestroyed && deps.length === 0) { if (!_EditorInstanceManager.compareOptions(this.options.current, this.editor.options)) { this.editor.setOptions({ ...this.options.current, editable: this.editor.isEditable }); } } else { 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) { if (this.previousDeps === null) { this.previousDeps = deps; return; } const depsAreEqual = this.previousDeps.length === deps.length && this.previousDeps.every((dep, index) => dep === deps[index]); if (depsAreEqual) { return; } } if (this.editor && !this.editor.isDestroyed) { this.editor.destroy(); } this.setEditor(this.createEditor()); 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; this.scheduledDestructionTimeout = setTimeout(() => { if (this.isComponentMounted && this.instanceId === currentInstanceId) { if (currentEditor) { currentEditor.setOptions(this.options.current); } return; } if (currentEditor && !currentEditor.isDestroyed) { currentEditor.destroy(); if (this.instanceId === currentInstanceId) { this.setEditor(null); } } }, 1); } }; function useEditor(options = {}, deps = []) { const mostRecentOptions = useRef(options); mostRecentOptions.current = options; const [instanceManager] = useState2(() => new EditorInstanceManager(mostRecentOptions)); const editor = useSyncExternalStore2( instanceManager.subscribe, instanceManager.getEditor, instanceManager.getServerSnapshot ); useDebugValue2(editor); useEffect2(instanceManager.onRender(deps)); useEditorState({ editor, selector: ({ transactionNumber }) => { if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === void 0) { return null; } if (options.immediatelyRender && transactionNumber === 0) { return 0; } return transactionNumber + 1; } }); return editor; } // src/Context.tsx import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime"; var EditorContext = createContext({ editor: null }); var EditorConsumer = EditorContext.Consumer; var useCurrentEditor = () => useContext(EditorContext); function EditorProvider({ children, slotAfter, slotBefore, editorContainerProps = {}, ...editorOptions }) { const editor = useEditor(editorOptions); const contextValue = useMemo(() => ({ editor }), [editor]); if (!editor) { return null; } return /* @__PURE__ */ jsxs2(EditorContext.Provider, { value: contextValue, children: [ slotBefore, /* @__PURE__ */ jsx2(EditorConsumer, { children: ({ editor: currentEditor }) => /* @__PURE__ */ jsx2(EditorContent, { editor: currentEditor, ...editorContainerProps }) }), children, slotAfter ] }); } // src/useReactNodeView.ts import { createContext as createContext2, createElement, useContext as useContext2 } from "react"; var ReactNodeViewContext = createContext2({ onDragStart: () => { }, nodeViewContentChildren: void 0, nodeViewContentRef: () => { } }); var ReactNodeViewContentProvider = ({ children, content }) => { return createElement(ReactNodeViewContext.Provider, { value: { nodeViewContentChildren: content } }, children); }; var useReactNodeView = () => useContext2(ReactNodeViewContext); // src/NodeViewContent.tsx import { jsx as jsx3 } from "react/jsx-runtime"; function NodeViewContent({ as: Tag = "div", ...props }) { const { nodeViewContentRef, nodeViewContentChildren } = useReactNodeView(); return ( // @ts-ignore /* @__PURE__ */ jsx3( Tag, { ...props, ref: nodeViewContentRef, "data-node-view-content": "", style: { whiteSpace: "pre-wrap", ...props.style }, children: nodeViewContentChildren } ) ); } // src/NodeViewWrapper.tsx import React3 from "react"; import { jsx as jsx4 } from "react/jsx-runtime"; var NodeViewWrapper = React3.forwardRef((props, ref) => { const { onDragStart } = useReactNodeView(); const Tag = props.as || "div"; return ( // @ts-ignore /* @__PURE__ */ jsx4( Tag, { ...props, ref, "data-node-view-wrapper": "", onDragStart, style: { whiteSpace: "normal", ...props.style } } ) ); }); // src/ReactMarkViewRenderer.tsx import { MarkView } from "@tiptap/core"; import React4 from "react"; // src/ReactRenderer.tsx import { version as reactVersion } from "react"; import { flushSync } from "react-dom"; import { jsx as jsx5 } from "react/jsx-runtime"; function isClassComponent(Component) { return !!(typeof Component === "function" && Component.prototype && Component.prototype.isReactComponent); } function isForwardRefComponent(Component) { return !!(typeof Component === "object" && Component.$$typeof && (Component.$$typeof.toString() === "Symbol(react.forward_ref)" || Component.$$typeof.description === "react.forward_ref")); } function isMemoComponent(Component) { return !!(typeof Component === "object" && Component.$$typeof && (Component.$$typeof.toString() === "Symbol(react.memo)" || Component.$$typeof.description === "react.memo")); } function canReceiveRef(Component) { if (isClassComponent(Component)) { return true; } if (isForwardRefComponent(Component)) { return true; } if (isMemoComponent(Component)) { const wrappedComponent = Component.type; if (wrappedComponent) { return isClassComponent(wrappedComponent) || isForwardRefComponent(wrappedComponent); } } return false; } function isReact19Plus() { try { if (reactVersion) { const majorVersion = parseInt(reactVersion.split(".")[0], 10); return majorVersion >= 19; } } catch { } return false; } var ReactRenderer = class { /** * Immediately creates element and renders the provided React component. */ constructor(component, { editor, props = {}, as = "div", className = "" }) { this.ref = null; /** * Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown. */ this.destroyed = false; this.id = Math.floor(Math.random() * 4294967295).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 (this.editor.isInitialized) { flushSync(() => { this.render(); }); } else { queueMicrotask(() => { if (this.destroyed) { return; } this.render(); }); } } /** * Render the React component. */ render() { var _a; if (this.destroyed) { return; } const Component = this.component; const props = this.props; const editor = this.editor; const isReact19 = isReact19Plus(); const componentCanReceiveRef = canReceiveRef(Component); const elementProps = { ...props }; if (elementProps.ref && !(isReact19 || componentCanReceiveRef)) { delete elementProps.ref; } if (!elementProps.ref && (isReact19 || componentCanReceiveRef)) { elementProps.ref = (ref) => { this.ref = ref; }; } this.reactElement = /* @__PURE__ */ jsx5(Component, { ...elementProps }); (_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.setRenderer(this.id, this); } /** * Re-renders the React component with new props. */ updateProps(props = {}) { if (this.destroyed) { return; } this.props = { ...this.props, ...props }; this.render(); } /** * Destroy the React component. */ destroy() { var _a; this.destroyed = true; const editor = this.editor; (_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.removeRenderer(this.id); try { if (this.element && this.element.parentNode) { this.element.parentNode.removeChild(this.element); } } catch { } } /** * Update the attributes of the element that holds the React component. */ updateAttributes(attributes) { Object.keys(attributes).forEach((key) => { this.element.setAttribute(key, attributes[key]); }); } }; // src/ReactMarkViewRenderer.tsx import { jsx as jsx6 } from "react/jsx-runtime"; var ReactMarkViewContext = React4.createContext({ markViewContentRef: () => { } }); var MarkViewContent = (props) => { const { as: Tag = "span", ...rest } = props; const { markViewContentRef } = React4.useContext(ReactMarkViewContext); return ( // @ts-ignore /* @__PURE__ */ jsx6(Tag, { ...rest, ref: markViewContentRef, "data-mark-view-content": "" }) ); }; var ReactMarkView = class extends MarkView { constructor(component, props, options) { super(component, props, options); const { as = "span", attrs, className = "" } = options || {}; const componentProps = { ...props, updateAttributes: this.updateAttributes.bind(this) }; this.contentDOMElement = document.createElement("span"); const markViewContentRef = (el) => { if (el && !el.contains(this.contentDOMElement)) { el.appendChild(this.contentDOMElement); } }; const context = { markViewContentRef }; const ReactMarkViewProvider = React4.memo((componentProps2) => { return /* @__PURE__ */ jsx6(ReactMarkViewContext.Provider, { value: context, children: React4.createElement(component, componentProps2) }); }); ReactMarkViewProvider.displayName = "ReactMarkView"; this.renderer = new ReactRenderer(ReactMarkViewProvider, { editor: props.editor, props: componentProps, as, className: `mark-${props.mark.type.name} ${className}`.trim() }); if (attrs) { this.renderer.updateAttributes(attrs); } } get dom() { return this.renderer.element; } get contentDOM() { return this.contentDOMElement; } }; function ReactMarkViewRenderer(component, options = {}) { return (props) => new ReactMarkView(component, props, options); } // src/ReactNodeViewRenderer.tsx import { getRenderedAttributes, NodeView } from "@tiptap/core"; import { createElement as createElement2, createRef, memo } from "react"; import { jsx as jsx7 } from "react/jsx-runtime"; var ReactNodeView = class extends NodeView { constructor(component, props, options) { super(component, props, options); /** * The requestAnimationFrame ID used for selection updates. */ this.selectionRafId = null; this.cachedExtensionWithSyncedStorage = null; 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 = ""; this.contentDOMElement.style.whiteSpace = "inherit"; const contentTarget = this.dom.querySelector("[data-node-view-content]"); if (!contentTarget) { return; } contentTarget.appendChild(this.contentDOMElement); } } /** * Returns a proxy of the extension that redirects storage access to the editor's mutable storage. * This preserves the original prototype chain (instanceof checks, methods like configure/extend work). * Cached to avoid proxy creation on every update. */ get extensionWithSyncedStorage() { if (!this.cachedExtensionWithSyncedStorage) { const editor = this.editor; const extension = this.extension; this.cachedExtensionWithSyncedStorage = new Proxy(extension, { get(target, prop, receiver) { var _a; if (prop === "storage") { return (_a = editor.storage[extension.name]) != null ? _a : {}; } return Reflect.get(target, prop, receiver); } }); } return this.cachedExtensionWithSyncedStorage; } /** * 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.extensionWithSyncedStorage, HTMLAttributes: this.HTMLAttributes, getPos: () => this.getPos(), updateAttributes: (attributes = {}) => this.updateAttributes(attributes), deleteNode: () => this.deleteNode(), ref: 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) { 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; const ReactNodeViewProvider = memo((componentProps) => { return /* @__PURE__ */ jsx7(ReactNodeViewContext.Provider, { value: context, children: createElement2(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 ? 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() { if (this.selectionRafId) { cancelAnimationFrame(this.selectionRafId); this.selectionRafId = null; } this.selectionRafId = requestAnimationFrame(() => { this.selectionRafId = null; 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, extension: this.extensionWithSyncedStorage }) }); } 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, extension: this.extensionWithSyncedStorage }); 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; if (this.selectionRafId) { cancelAnimationFrame(this.selectionRafId); this.selectionRafId = 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 = getRenderedAttributes(this.node, extensionAttributes); attrsObj = this.options.attrs({ node: this.node, HTMLAttributes }); } else { attrsObj = this.options.attrs; } this.renderer.updateAttributes(attrsObj); } } }; function ReactNodeViewRenderer(component, options) { return (props) => { if (!props.editor.contentComponent) { return {}; } return new ReactNodeView(component, props, options); }; } // src/Tiptap.tsx import { createContext as createContext3, useContext as useContext3, useMemo as useMemo2 } from "react"; import { jsx as jsx8 } from "react/jsx-runtime"; var TiptapContext = createContext3({ get editor() { throw new Error("useTiptap must be used within a provider"); } }); TiptapContext.displayName = "TiptapContext"; var useTiptap = () => useContext3(TiptapContext); function useTiptapState(selector, equalityFn) { const { editor } = useTiptap(); return useEditorState({ editor, selector, equalityFn }); } function TiptapWrapper({ editor, instance, children }) { const resolvedEditor = editor != null ? editor : instance; if (!resolvedEditor) { throw new Error("Tiptap: An editor instance is required. Pass a non-null `editor` prop."); } const tiptapContextValue = useMemo2(() => ({ editor: resolvedEditor }), [resolvedEditor]); const legacyContextValue = useMemo2(() => ({ editor: resolvedEditor }), [resolvedEditor]); return /* @__PURE__ */ jsx8(EditorContext.Provider, { value: legacyContextValue, children: /* @__PURE__ */ jsx8(TiptapContext.Provider, { value: tiptapContextValue, children }) }); } TiptapWrapper.displayName = "Tiptap"; function TiptapContent({ ...rest }) { const { editor } = useTiptap(); return /* @__PURE__ */ jsx8(EditorContent, { editor, ...rest }); } TiptapContent.displayName = "Tiptap.Content"; var Tiptap = Object.assign(TiptapWrapper, { /** * The Tiptap Content component that renders the EditorContent with the editor instance from the context. * @see TiptapContent */ Content: TiptapContent }); // src/index.ts export * from "@tiptap/core"; export { EditorConsumer, EditorContent, EditorContext, EditorProvider, MarkViewContent, NodeViewContent, NodeViewWrapper, PureEditorContent, ReactMarkView, ReactMarkViewContext, ReactMarkViewRenderer, ReactNodeView, ReactNodeViewContentProvider, ReactNodeViewContext, ReactNodeViewRenderer, ReactRenderer, Tiptap, TiptapContent, TiptapContext, TiptapWrapper, useCurrentEditor, useEditor, useEditorState, useReactNodeView, useTiptap, useTiptapState }; //# sourceMappingURL=index.js.map