New:Socket for Asana Is Now Available.Learn more
Sign In

@tiptap/react

Package Overview
Dependencies
Maintainers
6
Versions
413
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tiptap/react - npm Package Compare versions

Comparing version
3.30.2
to
3.30.3
+1
dist/index.d.cts.map
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/useEditor.ts","../src/Context.tsx","../src/ReactRenderer.tsx","../src/Editor.ts","../src/EditorContent.tsx","../src/NodeViewContent.tsx","../src/NodeViewWrapper.tsx","../src/ReactMarkViewRenderer.tsx","../src/types.ts","../src/ReactNodeViewRenderer.tsx","../src/ReactWidgetRenderer.tsx","../src/Tiptap.tsx","../src/useEditorState.ts","../src/useReactNodeView.ts","../src/index.ts"],"mappings":"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"}
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/useEditor.ts","../src/Context.tsx","../src/ReactRenderer.tsx","../src/Editor.ts","../src/EditorContent.tsx","../src/NodeViewContent.tsx","../src/NodeViewWrapper.tsx","../src/ReactMarkViewRenderer.tsx","../src/types.ts","../src/ReactNodeViewRenderer.tsx","../src/ReactWidgetRenderer.tsx","../src/Tiptap.tsx","../src/useEditorState.ts","../src/useReactNodeView.ts","../src/index.ts"],"mappings":"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"}
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/menus/BubbleMenu.tsx","../../src/menus/FloatingMenu.tsx"],"mappings":";;;;KASK,WAAS,GAAG,gBAAgB,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,GAAG;KAExD,kBAAkB,WAC5B,KAAK,WAAS,6DAGd,MAAM,eAAe;cAEV,YAAU,MAAA,0BAAA,KAAA,QAAA,KAAA,WAAA,8DAAA,KAAA,KAAA,WAAA,6DAAA,MAAA,eAAA,kBAAA,MAAA,cAAA;;;KCPlB,SAAS,GAAG,gBAAgB,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,GAAG;KAExD,oBAAoB,KAC9B,SAAS;EAGT,QAAQ;EACR,UAAU;IACR,MAAM,eAAe;cAEZ,cAAY,MAAA,0BAAA,KAAA,SAAA;EAJf,QAAA;EACE,UAAA;IA+IX,MAAA,eAAA,kBAAA,MAAA,cAAA"}
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/menus/BubbleMenu.tsx","../../src/menus/FloatingMenu.tsx"],"mappings":";;;;KASK,WAAS,GAAG,gBAAgB,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,GAAG;KAExD,kBAAkB,WAC5B,KAAK,WAAS,6DAGd,MAAM,eAAe;cAEV,YAAU,MAAA,0BAAA,KAAA,QAAA,KAAA,WAAA,8DAAA,KAAA,KAAA,WAAA,6DAAA,MAAA,eAAA,kBAAA,MAAA,cAAA;;;KCPlB,SAAS,GAAG,gBAAgB,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,GAAG;KAExD,oBAAoB,KAC9B,SAAS;EAGT,QAAQ;EACR,UAAU;IACR,MAAM,eAAe;cAEZ,cAAY,MAAA,0BAAA,KAAA,SAAA;EAJf,QAAA;EACE,UAAA;IA+IX,MAAA,eAAA,kBAAA,MAAA,cAAA"}
+1234
-1274

@@ -1,3 +0,4 @@

"use strict";
"use client";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
//#region \0rolldown/runtime.js
var __create = Object.create;

@@ -9,1328 +10,1287 @@ var __defProp = Object.defineProperty;

var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
EditorConsumer: () => EditorConsumer,
EditorContent: () => EditorContent,
EditorContext: () => EditorContext,
EditorProvider: () => EditorProvider,
MarkViewContent: () => MarkViewContent,
NodeViewContent: () => NodeViewContent,
NodeViewWrapper: () => NodeViewWrapper,
PureEditorContent: () => PureEditorContent,
ReactMarkView: () => ReactMarkView,
ReactMarkViewContext: () => ReactMarkViewContext,
ReactMarkViewRenderer: () => ReactMarkViewRenderer,
ReactNodeView: () => ReactNodeView,
ReactNodeViewContentProvider: () => ReactNodeViewContentProvider,
ReactNodeViewContext: () => ReactNodeViewContext,
ReactNodeViewRenderer: () => ReactNodeViewRenderer,
ReactRenderer: () => ReactRenderer,
ReactWidgetRenderer: () => ReactWidgetRenderer,
Tiptap: () => Tiptap,
TiptapContent: () => TiptapContent,
TiptapContext: () => TiptapContext,
TiptapWrapper: () => TiptapWrapper,
createContentComponent: () => createContentComponent,
useCurrentEditor: () => useCurrentEditor,
useEditor: () => useEditor,
useEditorState: () => useEditorState,
useReactNodeView: () => useReactNodeView,
useTiptap: () => useTiptap,
useTiptapState: () => useTiptapState
});
module.exports = __toCommonJS(index_exports);
// src/Context.tsx
var import_react4 = require("react");
// src/EditorContent.tsx
var import_react = __toESM(require("react"), 1);
var import_react_dom = __toESM(require("react-dom"), 1);
var import_shim = require("use-sync-external-store/shim/index.js");
var import_jsx_runtime = require("react/jsx-runtime");
var mergeRefs = (...refs) => {
return (node) => {
refs.forEach((ref) => {
if (typeof ref === "function") {
ref(node);
} else if (ref) {
;
ref.current = node;
}
});
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let react = require("react");
react = __toESM(react, 1);
let react_dom = require("react-dom");
react_dom = __toESM(react_dom, 1);
let use_sync_external_store_shim_index_js = require("use-sync-external-store/shim/index.js");
let react_jsx_runtime = require("react/jsx-runtime");
let _tiptap_core = require("@tiptap/core");
let fast_equals = require("fast-equals");
let use_sync_external_store_shim_with_selector_js = require("use-sync-external-store/shim/with-selector.js");
//#region src/EditorContent.tsx
const mergeRefs = (...refs) => {
return (node) => {
refs.forEach((ref) => {
if (typeof ref === "function") ref(node);
else if (ref) ref.current = node;
});
};
};
var Portals = ({ contentComponent }) => {
const renderers = (0, import_shim.useSyncExternalStore)(
contentComponent.subscribe,
contentComponent.getSnapshot,
contentComponent.getServerSnapshot
);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: Object.values(renderers) });
/**
* This component renders all of the editor's node views.
*/
const Portals = ({ contentComponent }) => {
const renderers = (0, use_sync_external_store_shim_index_js.useSyncExternalStore)(contentComponent.subscribe, contentComponent.getSnapshot, contentComponent.getServerSnapshot);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: Object.values(renderers) });
};
function createContentComponent() {
const subscribers = /* @__PURE__ */ new Set();
let renderers = {};
let isNotificationQueued = false;
const notifySubscribers = () => {
if (isNotificationQueued || !subscribers.size) {
return;
}
isNotificationQueued = true;
queueMicrotask(() => {
isNotificationQueued = false;
subscribers.forEach((subscriber) => subscriber());
});
};
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]: import_react_dom.default.createPortal(renderer.reactElement, renderer.element, id)
};
notifySubscribers();
},
/**
* Removes a NodeView Renderer from the editor.
*/
removeRenderer(id) {
const nextRenderers = { ...renderers };
delete nextRenderers[id];
renderers = nextRenderers;
notifySubscribers();
}
};
const subscribers = /* @__PURE__ */ new Set();
let renderers = {};
let isNotificationQueued = false;
const notifySubscribers = () => {
if (isNotificationQueued || !subscribers.size) return;
isNotificationQueued = true;
queueMicrotask(() => {
isNotificationQueued = false;
subscribers.forEach((subscriber) => subscriber());
});
};
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]: react_dom.default.createPortal(renderer.reactElement, renderer.element, id)
};
notifySubscribers();
},
/**
* Removes a NodeView Renderer from the editor.
*/
removeRenderer(id) {
const nextRenderers = { ...renderers };
delete nextRenderers[id];
renderers = nextRenderers;
notifySubscribers();
}
};
}
var PureEditorContent = class extends import_react.default.Component {
constructor(props) {
super(props);
this.editorContentRef = import_react.default.createRef();
}
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 = createContentComponent();
editor.createNodeViews();
editor.isEditorContentInitialized = true;
this.forceUpdate();
}
}
componentWillUnmount() {
var _a;
const editor = this.props.editor;
if (!editor) {
return;
}
editor.isEditorContentInitialized = false;
if (!editor.isDestroyed) {
editor.view.setProps({
nodeViews: {}
});
}
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__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }),
(editor == null ? void 0 : editor.contentComponent) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Portals, { contentComponent: editor.contentComponent })
] });
}
var PureEditorContent = class extends react.default.Component {
constructor(props) {
super(props);
this.editorContentRef = react.default.createRef();
}
componentDidMount() {
this.init();
}
componentDidUpdate() {
this.init();
}
init() {
var _editor$view$dom;
const editor = this.props.editor;
if (editor && !editor.isDestroyed && ((_editor$view$dom = editor.view.dom) === null || _editor$view$dom === void 0 ? void 0 : _editor$view$dom.parentNode)) {
if (editor.contentComponent) return;
const element = this.editorContentRef.current;
element.append(...editor.view.dom.parentNode.childNodes);
editor.setOptions({ element });
editor.contentComponent = createContentComponent();
editor.createNodeViews();
editor.isEditorContentInitialized = true;
this.forceUpdate();
}
}
componentWillUnmount() {
const editor = this.props.editor;
if (!editor) return;
editor.isEditorContentInitialized = false;
if (!editor.isDestroyed) editor.view.setProps({ nodeViews: {} });
editor.contentComponent = null;
try {
var _editor$view$dom2;
if (!((_editor$view$dom2 = editor.view.dom) === null || _editor$view$dom2 === void 0 ? void 0 : _editor$view$dom2.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__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: mergeRefs(innerRef, this.editorContentRef),
...rest
}), (editor === null || editor === void 0 ? void 0 : editor.contentComponent) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Portals, { contentComponent: editor.contentComponent })] });
}
};
var EditorContentWithKey = (0, import_react.forwardRef)(
(props, ref) => {
const key = import_react.default.useMemo(() => {
return Math.floor(Math.random() * 4294967295).toString();
}, [props.editor]);
return import_react.default.createElement(PureEditorContent, {
key,
innerRef: ref,
...props
});
}
);
var EditorContent = import_react.default.memo(EditorContentWithKey);
// src/useEditor.ts
var import_core = require("@tiptap/core");
var import_react3 = require("react");
var import_shim2 = require("use-sync-external-store/shim/index.js");
// src/useEditorState.ts
var import_fast_equals = require("fast-equals");
var import_react2 = require("react");
var import_with_selector = require("use-sync-external-store/shim/with-selector.js");
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? import_react2.useLayoutEffect : import_react2.useEffect;
const EditorContentWithKey = (0, react.forwardRef)((props, ref) => {
const key = react.default.useMemo(() => {
return Math.floor(Math.random() * 4294967295).toString();
}, [props.editor]);
return react.default.createElement(PureEditorContent, {
key,
innerRef: ref,
...props
});
});
const EditorContent = react.default.memo(EditorContentWithKey);
//#endregion
//#region src/useEditorState.ts
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.
*/
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) {
let lastTransaction;
const fn = (props) => {
if ((props == null ? void 0 : props.transaction) !== void 0 && props.transaction === lastTransaction) {
return;
}
lastTransaction = props == null ? void 0 : props.transaction;
this.transactionNumber += 1;
this.subscribers.forEach((callback) => callback());
};
const currentEditor = this.editor;
currentEditor.on("transaction", fn);
currentEditor.on("update", fn);
return () => {
currentEditor.off("transaction", fn);
currentEditor.off("update", fn);
};
}
return void 0;
}
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) {
/**
* 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.
*/
let lastTransaction;
const fn = (props) => {
if ((props === null || props === void 0 ? void 0 : props.transaction) !== void 0 && props.transaction === lastTransaction) return;
lastTransaction = props === null || props === void 0 ? void 0 : props.transaction;
this.transactionNumber += 1;
this.subscribers.forEach((callback) => callback());
};
const currentEditor = this.editor;
currentEditor.on("transaction", fn);
currentEditor.on("update", fn);
return () => {
currentEditor.off("transaction", fn);
currentEditor.off("update", fn);
};
}
}
};
/**
* 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] = (0, import_react2.useState)(() => new EditorStateManager(options.editor));
const selectedState = (0, import_with_selector.useSyncExternalStoreWithSelector)(
editorStateManager.subscribe,
editorStateManager.getSnapshot,
editorStateManager.getServerSnapshot,
options.selector,
(_a = options.equalityFn) != null ? _a : import_fast_equals.deepEqual
);
useIsomorphicLayoutEffect(() => {
return editorStateManager.watch(options.editor);
}, [options.editor, editorStateManager]);
(0, import_react2.useDebugValue)(selectedState);
return selectedState;
var _options$equalityFn;
const [editorStateManager] = (0, react.useState)(() => new EditorStateManager(options.editor));
const selectedState = (0, use_sync_external_store_shim_with_selector_js.useSyncExternalStoreWithSelector)(editorStateManager.subscribe, editorStateManager.getSnapshot, editorStateManager.getServerSnapshot, options.selector, (_options$equalityFn = options.equalityFn) !== null && _options$equalityFn !== void 0 ? _options$equalityFn : fast_equals.deepEqual);
useIsomorphicLayoutEffect(() => {
return editorStateManager.watch(options.editor);
}, [options.editor, editorStateManager]);
(0, react.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() {
const explicit = this.options.current.immediatelyRender;
let immediatelyRender = explicit != null ? explicit : true;
if (isSSR) {
if (immediatelyRender && isDev) {
console.warn(
"SSR detected. `immediatelyRender` has been set to false to avoid hydration mismatches"
);
}
immediatelyRender = false;
} else if (isNext && explicit === void 0) {
immediatelyRender = false;
if (isDev) {
console.warn(
"Next.js detected. `immediatelyRender` defaults to false to avoid hydration mismatches. Pass `immediatelyRender: true` explicitly if you are rendering the editor only on the client."
);
}
}
return immediatelyRender ? this.createEditor() : 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);
},
onMount: (...args) => {
var _a, _b;
return (_b = (_a = this.options.current).onMount) == null ? void 0 : _b.call(_a, ...args);
},
onUnmount: (...args) => {
var _a, _b;
return (_b = (_a = this.options.current).onUnmount) == null ? void 0 : _b.call(_a, ...args);
}
};
const editor = new import_core.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);
}
//#endregion
//#region src/useEditor.ts
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.
*/
var EditorInstanceManager = class EditorInstanceManager {
constructor(options) {
this.editor = null;
this.subscriptions = /* @__PURE__ */ new Set();
this.isComponentMounted = false;
this.previousDeps = null;
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() {
const explicit = this.options.current.immediatelyRender;
let immediatelyRender = explicit !== null && explicit !== void 0 ? explicit : true;
if (isSSR) {
if (immediatelyRender && isDev) console.warn("SSR detected. `immediatelyRender` has been set to false to avoid hydration mismatches");
immediatelyRender = false;
} else if (isNext && explicit === void 0) {
immediatelyRender = false;
if (isDev) console.warn("Next.js detected. `immediatelyRender` defaults to false to avoid hydration mismatches. Pass `immediatelyRender: true` explicitly if you are rendering the editor only on the client.");
}
return immediatelyRender ? this.createEditor() : null;
}
/**
* Create a new editor instance. And attach event listeners.
*/
createEditor() {
return new _tiptap_core.Editor({
...this.options.current,
onBeforeCreate: (...args) => {
var _this$options$current, _this$options$current2;
return (_this$options$current = (_this$options$current2 = this.options.current).onBeforeCreate) === null || _this$options$current === void 0 ? void 0 : _this$options$current.call(_this$options$current2, ...args);
},
onBlur: (...args) => {
var _this$options$current3, _this$options$current4;
return (_this$options$current3 = (_this$options$current4 = this.options.current).onBlur) === null || _this$options$current3 === void 0 ? void 0 : _this$options$current3.call(_this$options$current4, ...args);
},
onCreate: (...args) => {
var _this$options$current5, _this$options$current6;
return (_this$options$current5 = (_this$options$current6 = this.options.current).onCreate) === null || _this$options$current5 === void 0 ? void 0 : _this$options$current5.call(_this$options$current6, ...args);
},
onDestroy: (...args) => {
var _this$options$current7, _this$options$current8;
return (_this$options$current7 = (_this$options$current8 = this.options.current).onDestroy) === null || _this$options$current7 === void 0 ? void 0 : _this$options$current7.call(_this$options$current8, ...args);
},
onFocus: (...args) => {
var _this$options$current9, _this$options$current10;
return (_this$options$current9 = (_this$options$current10 = this.options.current).onFocus) === null || _this$options$current9 === void 0 ? void 0 : _this$options$current9.call(_this$options$current10, ...args);
},
onSelectionUpdate: (...args) => {
var _this$options$current11, _this$options$current12;
return (_this$options$current11 = (_this$options$current12 = this.options.current).onSelectionUpdate) === null || _this$options$current11 === void 0 ? void 0 : _this$options$current11.call(_this$options$current12, ...args);
},
onTransaction: (...args) => {
var _this$options$current13, _this$options$current14;
return (_this$options$current13 = (_this$options$current14 = this.options.current).onTransaction) === null || _this$options$current13 === void 0 ? void 0 : _this$options$current13.call(_this$options$current14, ...args);
},
onUpdate: (...args) => {
var _this$options$current15, _this$options$current16;
return (_this$options$current15 = (_this$options$current16 = this.options.current).onUpdate) === null || _this$options$current15 === void 0 ? void 0 : _this$options$current15.call(_this$options$current16, ...args);
},
onContentError: (...args) => {
var _this$options$current17, _this$options$current18;
return (_this$options$current17 = (_this$options$current18 = this.options.current).onContentError) === null || _this$options$current17 === void 0 ? void 0 : _this$options$current17.call(_this$options$current18, ...args);
},
onDrop: (...args) => {
var _this$options$current19, _this$options$current20;
return (_this$options$current19 = (_this$options$current20 = this.options.current).onDrop) === null || _this$options$current19 === void 0 ? void 0 : _this$options$current19.call(_this$options$current20, ...args);
},
onPaste: (...args) => {
var _this$options$current21, _this$options$current22;
return (_this$options$current21 = (_this$options$current22 = this.options.current).onPaste) === null || _this$options$current21 === void 0 ? void 0 : _this$options$current21.call(_this$options$current22, ...args);
},
onDelete: (...args) => {
var _this$options$current23, _this$options$current24;
return (_this$options$current23 = (_this$options$current24 = this.options.current).onDelete) === null || _this$options$current23 === void 0 ? void 0 : _this$options$current23.call(_this$options$current24, ...args);
},
onMount: (...args) => {
var _this$options$current25, _this$options$current26;
return (_this$options$current25 = (_this$options$current26 = this.options.current).onMount) === null || _this$options$current25 === void 0 ? void 0 : _this$options$current25.call(_this$options$current26, ...args);
},
onUnmount: (...args) => {
var _this$options$current27, _this$options$current28;
return (_this$options$current27 = (_this$options$current28 = this.options.current).onUnmount) === null || _this$options$current27 === void 0 ? void 0 : _this$options$current27.call(_this$options$current28, ...args);
}
});
}
/**
* 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 _b$extensions;
if (extension !== ((_b$extensions = b.extensions) === null || _b$extensions === void 0 ? void 0 : _b$extensions[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;
}
if (this.previousDeps.length === deps.length && this.previousDeps.every((dep, index) => dep === deps[index])) 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 = (0, import_react3.useRef)(options);
mostRecentOptions.current = options;
const [instanceManager] = (0, import_react3.useState)(() => new EditorInstanceManager(mostRecentOptions));
const editor = (0, import_shim2.useSyncExternalStore)(
instanceManager.subscribe,
instanceManager.getEditor,
instanceManager.getServerSnapshot
);
(0, import_react3.useDebugValue)(editor);
(0, import_react3.useEffect)(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;
const mostRecentOptions = (0, react.useRef)(options);
mostRecentOptions.current = options;
const [instanceManager] = (0, react.useState)(() => new EditorInstanceManager(mostRecentOptions));
const editor = (0, use_sync_external_store_shim_index_js.useSyncExternalStore)(instanceManager.subscribe, instanceManager.getEditor, instanceManager.getServerSnapshot);
(0, react.useDebugValue)(editor);
(0, react.useEffect)(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
var import_jsx_runtime2 = require("react/jsx-runtime");
var EditorContext = (0, import_react4.createContext)({
editor: null
});
var EditorConsumer = EditorContext.Consumer;
var useCurrentEditor = () => (0, import_react4.useContext)(EditorContext);
function EditorProvider({
children,
slotAfter,
slotBefore,
editorContainerProps = {},
...editorOptions
}) {
const editor = useEditor(editorOptions);
const contextValue = (0, import_react4.useMemo)(() => ({ editor }), [editor]);
if (!editor) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(EditorContext.Provider, { value: contextValue, children: [
slotBefore,
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EditorConsumer, { children: ({ editor: currentEditor }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EditorContent, { editor: currentEditor, ...editorContainerProps }) }),
children,
slotAfter
] });
//#endregion
//#region src/Context.tsx
const EditorContext = (0, react.createContext)({ editor: null });
const EditorConsumer = EditorContext.Consumer;
/**
* A hook to get the current editor instance.
*/
const useCurrentEditor = () => (0, 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);
const contextValue = (0, react.useMemo)(() => ({ editor }), [editor]);
if (!editor) return null;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(EditorContext.Provider, {
value: contextValue,
children: [
slotBefore,
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditorConsumer, { children: ({ editor: currentEditor }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditorContent, {
editor: currentEditor,
...editorContainerProps
}) }),
children,
slotAfter
]
});
}
// src/useReactNodeView.ts
var import_react5 = require("react");
var ReactNodeViewContext = (0, import_react5.createContext)({
onDragStart: () => {
},
nodeViewContentChildren: void 0,
nodeViewContentRef: () => {
}
//#endregion
//#region src/useReactNodeView.ts
const ReactNodeViewContext = (0, react.createContext)({
onDragStart: () => {},
nodeViewContentChildren: void 0,
nodeViewContentRef: () => {}
});
var ReactNodeViewContentProvider = ({
children,
content
}) => {
return (0, import_react5.createElement)(
ReactNodeViewContext.Provider,
{ value: { nodeViewContentChildren: content } },
children
);
const ReactNodeViewContentProvider = ({ children, content }) => {
return (0, react.createElement)(ReactNodeViewContext.Provider, { value: { nodeViewContentChildren: content } }, children);
};
var useReactNodeView = () => (0, import_react5.useContext)(ReactNodeViewContext);
// src/NodeViewContent.tsx
var import_jsx_runtime3 = (
// @ts-ignore
require("react/jsx-runtime")
);
function NodeViewContent({
as: Tag = "div",
...props
}) {
const { nodeViewContentRef, nodeViewContentChildren } = useReactNodeView();
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
Tag,
{
...props,
ref: nodeViewContentRef,
"data-node-view-content": "",
style: {
whiteSpace: "pre-wrap",
...props.style
},
children: nodeViewContentChildren
}
);
const useReactNodeView = () => (0, react.useContext)(ReactNodeViewContext);
//#endregion
//#region src/NodeViewContent.tsx
function NodeViewContent({ as: Tag = "div", ...props }) {
const { nodeViewContentRef, nodeViewContentChildren } = useReactNodeView();
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tag, {
...props,
ref: nodeViewContentRef,
"data-node-view-content": "",
style: {
whiteSpace: "pre-wrap",
...props.style
},
children: nodeViewContentChildren
});
}
// src/NodeViewWrapper.tsx
var import_react6 = __toESM(require("react"), 1);
var import_jsx_runtime4 = (
// @ts-ignore
require("react/jsx-runtime")
);
var NodeViewWrapper = import_react6.default.forwardRef((props, ref) => {
const { onDragStart } = useReactNodeView();
const Tag = props.as || "div";
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
Tag,
{
...props,
ref,
"data-node-view-wrapper": "",
onDragStart,
style: {
whiteSpace: "normal",
...props.style
}
}
);
//#endregion
//#region src/NodeViewWrapper.tsx
const NodeViewWrapper = react.default.forwardRef((props, ref) => {
const { onDragStart } = useReactNodeView();
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(props.as || "div", {
...props,
ref,
"data-node-view-wrapper": "",
onDragStart,
style: {
whiteSpace: "normal",
...props.style
}
});
});
// src/ReactMarkViewRenderer.tsx
var import_core2 = require("@tiptap/core");
var import_react8 = __toESM(require("react"), 1);
// src/ReactRenderer.tsx
var import_react7 = require("react");
var import_react_dom2 = require("react-dom");
var import_jsx_runtime5 = require("react/jsx-runtime");
//#endregion
//#region src/ReactRenderer.tsx
/**
* Check if a component is a class component.
* @param Component
* @returns {boolean}
*/
function isClassComponent(Component) {
return !!(typeof Component === "function" && Component.prototype && Component.prototype.isReactComponent);
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"));
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"));
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) {
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;
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;
}
/**
* Check if we're running React 19+ by detecting if function components support ref props
* @returns {boolean}
*/
function isReact19Plus() {
try {
if (import_react7.version) {
const majorVersion = parseInt(import_react7.version.split(".")[0], 10);
return majorVersion >= 19;
}
} catch {
}
return false;
try {
if (react.version) return parseInt(react.version.split(".")[0], 10) >= 19;
} catch {}
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',
* })
*/
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.isEditorContentInitialized) {
(0, import_react_dom2.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__ */ (0, import_jsx_runtime5.jsx)(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.
* Skips the render if none of the supplied props actually changed,
* to avoid unnecessary portal re-creation.
*/
updateProps(props = {}) {
if (this.destroyed) {
return;
}
let changed = false;
const keys = Object.keys(props);
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i];
if (props[key] !== this.props[key]) {
changed = true;
break;
}
}
if (!changed) {
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]);
});
}
/**
* Immediately creates element and renders the provided React component.
*/
constructor(component, { editor, props = {}, as = "div", className = "" }) {
this.ref = null;
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.isEditorContentInitialized) (0, react_dom.flushSync)(() => {
this.render();
});
else queueMicrotask(() => {
if (this.destroyed) return;
this.render();
});
}
/**
* Render the React component.
*/
render() {
var _editor$contentCompon;
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__ */ (0, react_jsx_runtime.jsx)(Component, { ...elementProps });
editor === null || editor === void 0 || (_editor$contentCompon = editor.contentComponent) === null || _editor$contentCompon === void 0 || _editor$contentCompon.setRenderer(this.id, this);
}
/**
* Re-renders the React component with new props.
* Skips the render if none of the supplied props actually changed,
* to avoid unnecessary portal re-creation.
*/
updateProps(props = {}) {
if (this.destroyed) return;
let changed = false;
const keys = Object.keys(props);
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i];
if (props[key] !== this.props[key]) {
changed = true;
break;
}
}
if (!changed) return;
this.props = {
...this.props,
...props
};
this.render();
}
/**
* Destroy the React component.
*/
destroy() {
var _editor$contentCompon2;
this.destroyed = true;
const editor = this.editor;
editor === null || editor === void 0 || (_editor$contentCompon2 = editor.contentComponent) === null || _editor$contentCompon2 === void 0 || _editor$contentCompon2.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
var import_jsx_runtime6 = (
// @ts-ignore
require("react/jsx-runtime")
);
var ReactMarkViewContext = import_react8.default.createContext({
markViewContentRef: () => {
}
});
var MarkViewContent = (props) => {
const { as: Tag = "span", ...rest } = props;
const { markViewContentRef } = import_react8.default.useContext(ReactMarkViewContext);
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Tag, { ...rest, ref: markViewContentRef, "data-mark-view-content": "" });
//#endregion
//#region src/ReactMarkViewRenderer.tsx
const ReactMarkViewContext = react.default.createContext({ markViewContentRef: () => {} });
const MarkViewContent = (props) => {
const { as: Tag = "span", ...rest } = props;
const { markViewContentRef } = react.default.useContext(ReactMarkViewContext);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tag, {
...rest,
ref: markViewContentRef,
"data-mark-view-content": ""
});
};
var ReactMarkView = class extends import_core2.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 = import_react8.default.memo(
(componentProps2) => {
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ReactMarkViewContext.Provider, { value: context, children: import_react8.default.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;
}
var ReactMarkView = class extends _tiptap_core.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 = react.default.memo((componentProps) => {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactMarkViewContext.Provider, {
value: context,
children: react.default.createElement(component, componentProps)
});
});
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);
return (props) => new ReactMarkView(component, props, options);
}
// src/ReactNodeViewRenderer.tsx
var import_core3 = require("@tiptap/core");
var import_react9 = require("react");
// src/captureDOMSelection.ts
//#endregion
//#region src/captureDOMSelection.ts
/**
* Saves the DOM selection inside `element`, returns a function to put it back.
* Browsers drop the selection when an element moves, so restore it after the move.
*/
function captureDOMSelection(element) {
var _a, _b;
const root = element.getRootNode();
const selection = typeof root.getSelection === "function" ? root.getSelection() : (_b = (_a = element.ownerDocument) == null ? void 0 : _a.defaultView) == null ? void 0 : _b.getSelection();
if (!selection || selection.rangeCount === 0) {
return null;
}
const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
if (!anchorNode || !focusNode) {
return null;
}
if (!element.contains(anchorNode) || !element.contains(focusNode)) {
return null;
}
return () => {
try {
selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
} catch {
}
};
var _element$ownerDocumen;
const root = element.getRootNode();
const selection = typeof root.getSelection === "function" ? root.getSelection() : (_element$ownerDocumen = element.ownerDocument) === null || _element$ownerDocumen === void 0 || (_element$ownerDocumen = _element$ownerDocumen.defaultView) === null || _element$ownerDocumen === void 0 ? void 0 : _element$ownerDocumen.getSelection();
if (!selection || selection.rangeCount === 0) return null;
const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
if (!anchorNode || !focusNode) return null;
if (!element.contains(anchorNode) || !element.contains(focusNode)) return null;
return () => {
try {
selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
} catch {}
};
}
// src/ReactNodeViewRenderer.tsx
var import_jsx_runtime7 = require("react/jsx-runtime");
var ReactNodeView = class extends import_core3.NodeView {
constructor(component, props, options) {
super(component, props, options);
/**
* The requestAnimationFrame ID used for selection updates.
*/
this.selectionRafId = null;
/**
* Fires on editor updates when trackNodeViewPosition is enabled.
* Detects position shifts where update() is NOT called (e.g. typing above).
*/
this.handlePositionUpdate = () => {
const newPos = this.getPos();
if (typeof newPos !== "number" || newPos === this.currentPos) {
return;
}
this.currentPos = newPos;
this.renderer.updateProps({ getPos: () => this.getPos() });
if (typeof this.options.attrs === "function") {
this.updateElementAttributes();
}
};
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) {
contentTarget.appendChild(this.contentDOMElement);
} else {
this.dom.appendChild(this.contentDOMElement);
}
}
if (this.options.trackNodeViewPosition) {
this.editor.on("update", this.handlePositionUpdate);
}
}
/**
* 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: (0, import_react9.createRef)()
};
const mountProps = props;
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");
}
const restoreSelection = captureDOMSelection(this.contentDOMElement);
element.appendChild(this.contentDOMElement);
restoreSelection == null ? void 0 : restoreSelection();
}
};
const context = { onDragStart, nodeViewContentRef };
const Component = this.component;
const ReactNodeViewProvider = (0, import_react9.memo)(
(componentProps) => {
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ReactNodeViewContext.Provider, { value: context, children: (0, import_react9.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: mountProps,
as,
className: `node-${this.node.type.name} ${className}`.trim()
});
this.editor.on("selectionUpdate", this.handleSelectionUpdate);
this.updateElementAttributes();
this.currentPos = this.getPos();
}
/**
* 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(() => {
var _a;
this.selectionRafId = null;
const pos = (_a = this.getPos()) != null ? _a : this.currentPos;
if (typeof pos !== "number") {
return;
}
const isSelected = (0, import_core3.isNodeViewSelected)({
selection: this.editor.state.selection,
pos,
nodeSize: this.node.nodeSize,
selectedOnTextSelection: this.options.selectedOnTextSelection
});
if (isSelected) {
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;
this.currentPos = this.getPos();
return this.options.update({
oldNode,
oldDecorations,
newNode: node,
newDecorations: decorations,
oldInnerDecorations,
innerDecorations,
updateProps: () => rerenderComponent({
node,
decorations,
innerDecorations,
extension: this.extensionWithSyncedStorage
})
});
}
const nodeChanged = node !== this.node;
if (!nodeChanged) {
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
return true;
}
const newPos = this.getPos();
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
this.currentPos = newPos;
const extraProps = {
node,
decorations,
innerDecorations,
extension: this.extensionWithSyncedStorage
};
if (this.options.trackNodeViewPosition) {
extraProps.getPos = () => this.getPos();
}
rerenderComponent(extraProps);
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);
if (this.options.trackNodeViewPosition) {
this.editor.off("update", this.handlePositionUpdate);
}
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 = (0, import_core3.getRenderedAttributes)(this.node, extensionAttributes);
attrsObj = this.options.attrs({ node: this.node, HTMLAttributes });
} else {
attrsObj = this.options.attrs;
}
this.renderer.updateAttributes(attrsObj);
}
}
//#endregion
//#region src/ReactNodeViewRenderer.tsx
var ReactNodeView = class extends _tiptap_core.NodeView {
constructor(component, props, options) {
super(component, props, options);
this.selectionRafId = null;
this.handlePositionUpdate = () => {
const newPos = this.getPos();
if (typeof newPos !== "number" || newPos === this.currentPos) return;
this.currentPos = newPos;
this.renderer.updateProps({ getPos: () => this.getPos() });
if (typeof this.options.attrs === "function") this.updateElementAttributes();
};
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) contentTarget.appendChild(this.contentDOMElement);
else this.dom.appendChild(this.contentDOMElement);
}
if (this.options.trackNodeViewPosition) this.editor.on("update", this.handlePositionUpdate);
}
/**
* 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) {
if (prop === "storage") {
var _editor$storage;
return (_editor$storage = editor.storage[extension.name]) !== null && _editor$storage !== void 0 ? _editor$storage : {};
}
return Reflect.get(target, prop, receiver);
} });
}
return this.cachedExtensionWithSyncedStorage;
}
/**
* Setup the React component.
* Called on initialization.
*/
mount() {
const mountProps = {
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: (0, 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) {
if (element.hasAttribute("data-node-view-wrapper")) element.removeAttribute("data-node-view-wrapper");
const restoreSelection = captureDOMSelection(this.contentDOMElement);
element.appendChild(this.contentDOMElement);
restoreSelection === null || restoreSelection === void 0 || restoreSelection();
}
};
const context = {
onDragStart,
nodeViewContentRef
};
const Component = this.component;
const ReactNodeViewProvider = (0, react.memo)((componentProps) => {
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReactNodeViewContext.Provider, {
value: context,
children: (0, 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: mountProps,
as,
className: `node-${this.node.type.name} ${className}`.trim()
});
this.editor.on("selectionUpdate", this.handleSelectionUpdate);
this.updateElementAttributes();
this.currentPos = this.getPos();
}
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom() {
var _this$renderer$elemen;
if (this.renderer.element.firstElementChild && !((_this$renderer$elemen = this.renderer.element.firstElementChild) === null || _this$renderer$elemen === void 0 ? void 0 : _this$renderer$elemen.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(() => {
var _this$getPos;
this.selectionRafId = null;
const pos = (_this$getPos = this.getPos()) !== null && _this$getPos !== void 0 ? _this$getPos : this.currentPos;
if (typeof pos !== "number") return;
if ((0, _tiptap_core.isNodeViewSelected)({
selection: this.editor.state.selection,
pos,
nodeSize: this.node.nodeSize,
selectedOnTextSelection: this.options.selectedOnTextSelection
})) {
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;
this.currentPos = this.getPos();
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.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
return true;
}
const newPos = this.getPos();
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
this.currentPos = newPos;
const extraProps = {
node,
decorations,
innerDecorations,
extension: this.extensionWithSyncedStorage
};
if (this.options.trackNodeViewPosition) extraProps.getPos = () => this.getPos();
rerenderComponent(extraProps);
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);
if (this.options.trackNodeViewPosition) this.editor.off("update", this.handlePositionUpdate);
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 = (0, _tiptap_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) => {
if (!props.editor.contentComponent) {
return {};
}
return new ReactNodeView(component, props, options);
};
return (props) => {
if (!props.editor.contentComponent) return {
dom: document.createElement("span"),
contentDOM: null,
update: () => false,
destroy: () => {},
selectNode: () => {},
deselectNode: () => {},
stopEvent: () => false,
ignoreMutation: () => true
};
return new ReactNodeView(component, props, options);
};
}
// src/ReactWidgetRenderer.tsx
var import_core4 = require("@tiptap/core");
var WIDGET_CACHE = /* @__PURE__ */ Symbol("tiptapReactWidgetCache");
//#endregion
//#region src/ReactWidgetRenderer.tsx
const WIDGET_CACHE = Symbol("tiptapReactWidgetCache");
/**
* Renders a React component into a ProseMirror widget decoration.
* Reuses Tiptap's `ReactRenderer` so the component lives in the editor's
* React tree (context and hooks work as usual). Use a stable `key` for
* stateful widgets.
* @example
* addDecorations() {
* return {
* create: ({ editor, state }) =>
* findMatches(state.doc).map(match =>
* ReactWidgetRenderer(MyWidget, {
* editor, pos: match.pos, key: `match-${match.id}`,
* props: { label: match.label },
* }),
* ),
* }
* }
*/
function ReactWidgetRenderer(component, options) {
const { editor, props = {}, as = "span", className } = options;
return (0, import_core4.createWidgetDecoration)({
// Forwards editor, pos, key and the ProseMirror widget options unchanged.
...options,
props,
cacheKey: WIDGET_CACHE,
context: (getPos) => ({ editor, getPos }),
create: (renderProps) => new ReactRenderer(component, {
editor,
as,
className,
props: renderProps
}),
materialize: (renderer) => {
renderer.render();
return renderer.element;
}
});
const { editor, props = {}, as = "span", className } = options;
return (0, _tiptap_core.createWidgetDecoration)({
...options,
props,
cacheKey: WIDGET_CACHE,
context: (getPos) => ({
editor,
getPos
}),
create: (renderProps) => new ReactRenderer(component, {
editor,
as,
className,
props: renderProps
}),
materialize: (renderer) => {
renderer.render();
return renderer.element;
}
});
}
// src/Tiptap.tsx
var import_react10 = require("react");
var import_jsx_runtime8 = require("react/jsx-runtime");
var TiptapContext = (0, import_react10.createContext)({
get editor() {
throw new Error("useTiptap must be used within a <Tiptap> provider");
}
});
//#endregion
//#region src/Tiptap.tsx
/**
* React context that stores the current editor instance.
*
* Use `useTiptap()` to read from this context in child components.
*/
const TiptapContext = (0, react.createContext)({ get editor() {
throw new Error("useTiptap must be used within a <Tiptap> provider");
} });
TiptapContext.displayName = "TiptapContext";
var useTiptap = () => (0, import_react10.useContext)(TiptapContext);
/**
* Hook to read the Tiptap context and access the editor instance.
*
* This is a small convenience wrapper around `useContext(TiptapContext)`.
* The editor is always available when used within a `<Tiptap>` provider.
*
* @returns The current `TiptapContextType` value from the provider.
*
* @example
* ```tsx
* import { useTiptap } from '@tiptap/react'
*
* function Toolbar() {
* const { editor } = useTiptap()
*
* return (
* <button onClick={() => editor.chain().focus().toggleBold().run()}>
* Bold
* </button>
* )
* }
* ```
*/
const useTiptap = () => (0, react.useContext)(TiptapContext);
/**
* Select a slice of the editor state using the context-provided editor.
*
* This is a thin wrapper around `useEditorState` that reads the `editor`
* instance from `useTiptap()` so callers don't have to pass it manually.
*
* @typeParam TSelectorResult - The type returned by the selector.
* @param selector - Function that receives the editor state snapshot and
* returns the piece of state you want to subscribe to.
* @param equalityFn - Optional function to compare previous/next selected
* values and avoid unnecessary updates.
* @returns The selected slice of the editor state.
*
* @example
* ```tsx
* function WordCount() {
* const wordCount = useTiptapState(state => {
* const text = state.editor.state.doc.textContent
* return text.split(/\s+/).filter(Boolean).length
* })
*
* return <span>{wordCount} words</span>
* }
* ```
*/
function useTiptapState(selector, equalityFn) {
const { editor } = useTiptap();
return useEditorState({
editor,
selector,
equalityFn
});
const { editor } = useTiptap();
return useEditorState({
editor,
selector,
equalityFn
});
}
/**
* Top-level provider component that makes the editor instance available via
* React context to all child components.
*
* This component also provides backwards compatibility with the legacy
* `EditorContext`, so components using `useCurrentEditor()` will work
* inside a `<Tiptap>` provider.
*
* @param props - Component props.
* @returns A context provider element wrapping `children`.
*
* @example
* ```tsx
* import { Tiptap, useEditor } from '@tiptap/react'
*
* function App() {
* const editor = useEditor({ extensions: [...] })
*
* return (
* <Tiptap editor={editor}>
* <Toolbar />
* <Tiptap.Content />
* </Tiptap>
* )
* }
* ```
*/
function TiptapWrapper({ children, ...props }) {
const resolvedEditor = "editor" in props ? props.editor : props.instance;
if (!resolvedEditor) {
throw new Error("Tiptap: An editor instance is required. Pass a non-null `editor` prop.");
}
const tiptapContextValue = (0, import_react10.useMemo)(
() => ({ editor: resolvedEditor }),
[resolvedEditor]
);
const legacyContextValue = (0, import_react10.useMemo)(() => ({ editor: resolvedEditor }), [resolvedEditor]);
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EditorContext.Provider, { value: legacyContextValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TiptapContext.Provider, { value: tiptapContextValue, children }) });
const resolvedEditor = "editor" in props ? props.editor : props.instance;
if (!resolvedEditor) throw new Error("Tiptap: An editor instance is required. Pass a non-null `editor` prop.");
const tiptapContextValue = (0, react.useMemo)(() => ({ editor: resolvedEditor }), [resolvedEditor]);
const legacyContextValue = (0, react.useMemo)(() => ({ editor: resolvedEditor }), [resolvedEditor]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EditorContext.Provider, {
value: legacyContextValue,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TiptapContext.Provider, {
value: tiptapContextValue,
children
})
});
}
TiptapWrapper.displayName = "Tiptap";
/**
* Convenience component that renders `EditorContent` using the context-provided
* editor instance. Use this instead of manually passing the `editor` prop.
*
* @param props - All `EditorContent` props except `editor` and `ref`.
* @returns An `EditorContent` element bound to the context editor.
*
* @example
* ```tsx
* // inside a Tiptap provider
* <Tiptap.Content className="editor" />
* ```
*/
function TiptapContent({ ...rest }) {
const { editor } = useTiptap();
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(EditorContent, { editor, ...rest });
const { editor } = useTiptap();
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(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
/**
* Root `Tiptap` component. Use it as the provider for all child components.
*
* The exported object includes the `Content` subcomponent for rendering the
* editor content area.
*
* This component provides both the new `TiptapContext` (accessed via `useTiptap()`)
* and the legacy `EditorContext` (accessed via `useCurrentEditor()`) for
* backwards compatibility.
*
* For bubble menus and floating menus, import them separately from
* `@tiptap/react/menus` to keep floating-ui as an optional dependency.
*
* @example
* ```tsx
* import { Tiptap, useEditor } from '@tiptap/react'
* import { BubbleMenu } from '@tiptap/react/menus'
*
* function App() {
* const editor = useEditor({ extensions: [...] })
*
* return (
* <Tiptap editor={editor}>
* <Tiptap.Content />
* <BubbleMenu>
* <button onClick={() => editor.chain().focus().toggleBold().run()}>Bold</button>
* </BubbleMenu>
* </Tiptap>
* )
* }
* ```
*/
const Tiptap = Object.assign(TiptapWrapper, {
/**
* The Tiptap Content component that renders the EditorContent with the editor instance from the context.
* @see TiptapContent
*/
Content: TiptapContent });
//#endregion
exports.EditorConsumer = EditorConsumer;
exports.EditorContent = EditorContent;
exports.EditorContext = EditorContext;
exports.EditorProvider = EditorProvider;
exports.MarkViewContent = MarkViewContent;
exports.NodeViewContent = NodeViewContent;
exports.NodeViewWrapper = NodeViewWrapper;
exports.PureEditorContent = PureEditorContent;
exports.ReactMarkView = ReactMarkView;
exports.ReactMarkViewContext = ReactMarkViewContext;
exports.ReactMarkViewRenderer = ReactMarkViewRenderer;
exports.ReactNodeView = ReactNodeView;
exports.ReactNodeViewContentProvider = ReactNodeViewContentProvider;
exports.ReactNodeViewContext = ReactNodeViewContext;
exports.ReactNodeViewRenderer = ReactNodeViewRenderer;
exports.ReactRenderer = ReactRenderer;
exports.ReactWidgetRenderer = ReactWidgetRenderer;
exports.Tiptap = Tiptap;
exports.TiptapContent = TiptapContent;
exports.TiptapContext = TiptapContext;
exports.TiptapWrapper = TiptapWrapper;
exports.createContentComponent = createContentComponent;
exports.useCurrentEditor = useCurrentEditor;
exports.useEditor = useEditor;
exports.useEditorState = useEditorState;
exports.useReactNodeView = useReactNodeView;
exports.useTiptap = useTiptap;
exports.useTiptapState = useTiptapState;
Object.keys(_tiptap_core).forEach(function(k) {
if (k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function() {
return _tiptap_core[k];
}
});
});
// src/index.ts
__reExport(index_exports, require("@tiptap/core"), module.exports);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
EditorConsumer,
EditorContent,
EditorContext,
EditorProvider,
MarkViewContent,
NodeViewContent,
NodeViewWrapper,
PureEditorContent,
ReactMarkView,
ReactMarkViewContext,
ReactMarkViewRenderer,
ReactNodeView,
ReactNodeViewContentProvider,
ReactNodeViewContext,
ReactNodeViewRenderer,
ReactRenderer,
ReactWidgetRenderer,
Tiptap,
TiptapContent,
TiptapContext,
TiptapWrapper,
createContentComponent,
useCurrentEditor,
useEditor,
useEditorState,
useReactNodeView,
useTiptap,
useTiptapState,
...require("@tiptap/core")
});
//# sourceMappingURL=index.cjs.map

@@ -1,9 +0,8 @@

import * as react_jsx_runtime from 'react/jsx-runtime';
import { EditorOptions, Editor, MarkView, MarkViewProps, MarkViewRendererOptions, MarkViewRenderer, NodeViewProps, NodeViewRendererOptions, NodeView, NodeViewRendererProps, NodeViewRenderer, WidgetDecorationOptions, WidgetDecoration } from '@tiptap/core';
export * from '@tiptap/core';
import * as React from 'react';
import React__default, { DependencyList, ReactNode, HTMLAttributes, ComponentClass, FunctionComponent, ForwardRefExoticComponent, PropsWithoutRef, RefAttributes, ReactPortal, HTMLProps, ForwardedRef, ComponentProps, ComponentType as ComponentType$1 } from 'react';
import { Node } from '@tiptap/pm/model';
import { Decoration, DecorationSource } from '@tiptap/pm/view';
import { Editor, EditorOptions, MarkView, MarkViewProps, MarkViewRenderer, MarkViewRendererOptions, NodeView, NodeViewProps, NodeViewRenderer, NodeViewRendererOptions, NodeViewRendererProps, WidgetDecoration, WidgetDecorationOptions } from "@tiptap/core";
import React, { ComponentClass, ComponentProps, ComponentType, DependencyList, ForwardRefExoticComponent, ForwardedRef, FunctionComponent, HTMLAttributes, HTMLProps, PropsWithoutRef, ReactNode, ReactPortal, RefAttributes } from "react";
import { Node } from "@tiptap/pm/model";
import { Decoration, DecorationSource } from "@tiptap/pm/view";
export * from "@tiptap/core";
//#endregion
//#region src/useEditor.d.ts
/**

@@ -13,15 +12,15 @@ * The options for the `useEditor` hook.

type UseEditorOptions = Partial<EditorOptions> & {
/**
* Whether to render the editor on the first render.
* If client-side rendering, set this to `true`.
* If server-side rendering, set this to `false`.
* @default true
*/
immediatelyRender?: boolean;
/**
* Whether to re-render the editor on each transaction.
* This is legacy behavior that will be removed in future versions.
* @default false
*/
shouldRerenderOnTransaction?: boolean;
/**
* Whether to render the editor on the first render.
* If client-side rendering, set this to `true`.
* If server-side rendering, set this to `false`.
* @default true
*/
immediatelyRender?: boolean;
/**
* Whether to re-render the editor on each transaction.
* This is legacy behavior that will be removed in future versions.
* @default false
*/
shouldRerenderOnTransaction?: boolean;
};

@@ -36,3 +35,3 @@ /**

declare function useEditor(options: UseEditorOptions & {
immediatelyRender: false;
immediatelyRender: false;
}, deps?: DependencyList): Editor | null;

@@ -47,8 +46,9 @@ /**

declare function useEditor(options: UseEditorOptions, deps?: DependencyList): Editor;
//#endregion
//#region src/Context.d.ts
type EditorContextValue = {
editor: Editor | null;
editor: Editor | null;
};
declare const EditorContext: React__default.Context<EditorContextValue>;
declare const EditorConsumer: React__default.Consumer<EditorContextValue>;
declare const EditorContext: React.Context<EditorContextValue>;
declare const EditorConsumer: React.Consumer<EditorContextValue>;
/**

@@ -59,6 +59,6 @@ * A hook to get the current editor instance.

type EditorProviderProps = {
children?: ReactNode;
slotBefore?: ReactNode;
slotAfter?: ReactNode;
editorContainerProps?: HTMLAttributes<HTMLDivElement>;
children?: ReactNode;
slotBefore?: ReactNode;
slotAfter?: ReactNode;
editorContainerProps?: HTMLAttributes<HTMLDivElement>;
} & UseEditorOptions;

@@ -70,31 +70,32 @@ /**

*/
declare function EditorProvider({ children, slotAfter, slotBefore, editorContainerProps, ...editorOptions }: EditorProviderProps): react_jsx_runtime.JSX.Element | null;
declare function EditorProvider({ children, slotAfter, slotBefore, editorContainerProps, ...editorOptions }: EditorProviderProps): React.JSX.Element | null;
//#endregion
//#region src/ReactRenderer.d.ts
interface ReactRendererOptions {
/**
* The editor instance.
* @type {Editor}
*/
editor: Editor;
/**
* The props for the component.
* @type {Record<string, any>}
* @default {}
*/
props?: Record<string, any>;
/**
* The tag name of the element.
* @type {string}
* @default 'div'
*/
as?: string;
/**
* The class name of the element.
* @type {string}
* @default ''
* @example 'foo bar'
*/
className?: string;
/**
* The editor instance.
* @type {Editor}
*/
editor: Editor;
/**
* The props for the component.
* @type {Record<string, any>}
* @default {}
*/
props?: Record<string, any>;
/**
* The tag name of the element.
* @type {string}
* @default 'div'
*/
as?: string;
/**
* The class name of the element.
* @type {string}
* @default ''
* @example 'foo bar'
*/
className?: string;
}
type ComponentType<R, P> = ComponentClass<P> | FunctionComponent<P> | ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<R>>;
type ComponentType$1<R, P> = ComponentClass<P> | FunctionComponent<P> | ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<R>>;
/**

@@ -112,216 +113,223 @@ * The ReactRenderer class. It's responsible for rendering React components inside the editor.

declare class ReactRenderer<R = unknown, P extends Record<string, any> = object> {
id: string;
editor: EditorWithContentComponent;
component: any;
element: HTMLElement;
props: P;
reactElement: ReactNode;
ref: R | null;
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
destroyed: boolean;
/**
* Immediately creates element and renders the provided React component.
*/
constructor(component: ComponentType<R, P>, { editor, props, as, className }: ReactRendererOptions);
/**
* Render the React component.
*/
render(): void;
/**
* Re-renders the React component with new props.
* Skips the render if none of the supplied props actually changed,
* to avoid unnecessary portal re-creation.
*/
updateProps(props?: Record<string, any>): void;
/**
* Destroy the React component.
*/
destroy(): void;
/**
* Update the attributes of the element that holds the React component.
*/
updateAttributes(attributes: Record<string, string>): void;
id: string;
editor: EditorWithContentComponent;
component: any;
element: HTMLElement;
props: P;
reactElement: ReactNode;
ref: R | null;
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
destroyed: boolean;
/**
* Immediately creates element and renders the provided React component.
*/
constructor(component: ComponentType$1<R, P>, { editor, props, as, className }: ReactRendererOptions);
/**
* Render the React component.
*/
render(): void;
/**
* Re-renders the React component with new props.
* Skips the render if none of the supplied props actually changed,
* to avoid unnecessary portal re-creation.
*/
updateProps(props?: Record<string, any>): void;
/**
* Destroy the React component.
*/
destroy(): void;
/**
* Update the attributes of the element that holds the React component.
*/
updateAttributes(attributes: Record<string, string>): void;
}
//#endregion
//#region src/Editor.d.ts
type EditorWithContentComponent = Editor & {
contentComponent?: ContentComponent | null;
isEditorContentInitialized?: boolean;
contentComponent?: ContentComponent | null;
isEditorContentInitialized?: boolean;
};
type ContentComponent = {
setRenderer(id: string, renderer: ReactRenderer): void;
removeRenderer(id: string): void;
subscribe: (callback: () => void) => () => void;
getSnapshot: () => Record<string, ReactPortal>;
getServerSnapshot: () => Record<string, ReactPortal>;
setRenderer(id: string, renderer: ReactRenderer): void;
removeRenderer(id: string): void;
subscribe: (callback: () => void) => () => void;
getSnapshot: () => Record<string, ReactPortal>;
getServerSnapshot: () => Record<string, ReactPortal>;
};
//#endregion
//#region src/EditorContent.d.ts
interface EditorContentProps extends HTMLProps<HTMLDivElement> {
editor: Editor | null;
innerRef?: ForwardedRef<HTMLDivElement | null>;
editor: Editor | null;
innerRef?: ForwardedRef<HTMLDivElement | null>;
}
declare function createContentComponent(): ContentComponent;
declare class PureEditorContent extends React__default.Component<EditorContentProps, {
hasContentComponentInitialized: boolean;
declare class PureEditorContent extends React.Component<EditorContentProps, {
hasContentComponentInitialized: boolean;
}> {
editorContentRef: React__default.RefObject<any>;
constructor(props: EditorContentProps);
componentDidMount(): void;
componentDidUpdate(): void;
init(): void;
componentWillUnmount(): void;
render(): react_jsx_runtime.JSX.Element;
editorContentRef: React.RefObject<any>;
constructor(props: EditorContentProps);
componentDidMount(): void;
componentDidUpdate(): void;
init(): void;
componentWillUnmount(): void;
render(): React.JSX.Element;
}
declare const EditorContent: React__default.NamedExoticComponent<Omit<EditorContentProps, "ref"> & React__default.RefAttributes<HTMLDivElement>>;
type NodeViewContentProps<T extends keyof React__default.JSX.IntrinsicElements = 'div'> = {
as?: NoInfer<T>;
declare const EditorContent: React.NamedExoticComponent<Omit<EditorContentProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
//#endregion
//#region src/NodeViewContent.d.ts
type NodeViewContentProps<T extends keyof React.JSX.IntrinsicElements = 'div'> = {
as?: NoInfer<T>;
} & ComponentProps<T>;
declare function NodeViewContent<T extends keyof React__default.JSX.IntrinsicElements = 'div'>({ as: Tag, ...props }: NodeViewContentProps<T>): react_jsx_runtime.JSX.Element;
declare function NodeViewContent<T extends keyof React.JSX.IntrinsicElements = 'div'>({ as: Tag, ...props }: NodeViewContentProps<T>): React.JSX.Element;
//#endregion
//#region src/NodeViewWrapper.d.ts
interface NodeViewWrapperProps {
[key: string]: any;
as?: React__default.ElementType;
[key: string]: any;
as?: React.ElementType;
}
declare const NodeViewWrapper: React__default.FC<NodeViewWrapperProps>;
declare const NodeViewWrapper: React.FC<NodeViewWrapperProps>;
//#endregion
//#region src/ReactMarkViewRenderer.d.ts
interface MarkViewContextProps {
markViewContentRef: (element: HTMLElement | null) => void;
markViewContentRef: (element: HTMLElement | null) => void;
}
declare const ReactMarkViewContext: React__default.Context<MarkViewContextProps>;
type MarkViewContentProps<T extends keyof React__default.JSX.IntrinsicElements = 'span'> = {
as?: T;
} & Omit<React__default.ComponentProps<T>, 'as'>;
declare const MarkViewContent: <T extends keyof React__default.JSX.IntrinsicElements = "span">(props: MarkViewContentProps<T>) => react_jsx_runtime.JSX.Element;
declare const ReactMarkViewContext: React.Context<MarkViewContextProps>;
type MarkViewContentProps<T extends keyof React.JSX.IntrinsicElements = 'span'> = {
as?: T;
} & Omit<React.ComponentProps<T>, 'as'>;
declare const MarkViewContent: <T extends keyof React.JSX.IntrinsicElements = "span">(props: MarkViewContentProps<T>) => React.JSX.Element;
interface ReactMarkViewRendererOptions extends MarkViewRendererOptions {
/**
* The tag name of the element wrapping the React component.
*/
as?: string;
className?: string;
attrs?: {
[key: string]: string;
};
/**
* The tag name of the element wrapping the React component.
*/
as?: string;
className?: string;
attrs?: {
[key: string]: string;
};
}
declare class ReactMarkView extends MarkView<React__default.ComponentType<MarkViewProps>, ReactMarkViewRendererOptions> {
renderer: ReactRenderer;
contentDOMElement: HTMLElement;
constructor(component: React__default.ComponentType<MarkViewProps>, props: MarkViewProps, options?: Partial<ReactMarkViewRendererOptions>);
get dom(): HTMLElement;
get contentDOM(): HTMLElement;
declare class ReactMarkView extends MarkView<React.ComponentType<MarkViewProps>, ReactMarkViewRendererOptions> {
renderer: ReactRenderer;
contentDOMElement: HTMLElement;
constructor(component: React.ComponentType<MarkViewProps>, props: MarkViewProps, options?: Partial<ReactMarkViewRendererOptions>);
get dom(): HTMLElement;
get contentDOM(): HTMLElement;
}
declare function ReactMarkViewRenderer(component: React__default.ComponentType<MarkViewProps>, options?: Partial<ReactMarkViewRendererOptions>): MarkViewRenderer;
declare function ReactMarkViewRenderer(component: React.ComponentType<MarkViewProps>, options?: Partial<ReactMarkViewRendererOptions>): MarkViewRenderer;
//#endregion
//#region src/types.d.ts
type ReactNodeViewProps<T = HTMLElement> = NodeViewProps & {
ref: React__default.RefObject<T | null>;
ref: React.RefObject<T | null>;
};
//#endregion
//#region src/ReactNodeViewRenderer.d.ts
interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
/**
* This function is called when the node view is updated.
* It allows you to compare the old node with the new node and decide if the component should update.
*/
update: ((props: {
oldNode: Node;
oldDecorations: readonly Decoration[];
oldInnerDecorations: DecorationSource;
newNode: Node;
newDecorations: readonly Decoration[];
innerDecorations: DecorationSource;
updateProps: () => void;
}) => boolean) | null;
/**
* The tag name of the element wrapping the React component.
*/
as?: string;
/**
* The class name of the element wrapping the React component.
*/
className?: string;
/**
* Attributes that should be applied to the element wrapping the React component.
* If this is a function, it will be called each time the node view is updated.
* If this is an object, it will be applied once when the node view is mounted.
*/
attrs?: Record<string, string> | ((props: {
node: Node;
HTMLAttributes: Record<string, any>;
}) => Record<string, string>);
/**
* This function is called when the node view is updated.
* It allows you to compare the old node with the new node and decide if the component should update.
*/
update: ((props: {
oldNode: Node;
oldDecorations: readonly Decoration[];
oldInnerDecorations: DecorationSource;
newNode: Node;
newDecorations: readonly Decoration[];
innerDecorations: DecorationSource;
updateProps: () => void;
}) => boolean) | null;
/**
* The tag name of the element wrapping the React component.
*/
as?: string;
/**
* The class name of the element wrapping the React component.
*/
className?: string;
/**
* Attributes that should be applied to the element wrapping the React component.
* If this is a function, it will be called each time the node view is updated.
* If this is an object, it will be applied once when the node view is mounted.
*/
attrs?: Record<string, string> | ((props: {
node: Node;
HTMLAttributes: Record<string, any>;
}) => Record<string, string>);
}
declare class ReactNodeView<T = HTMLElement, Component extends ComponentType$1<ReactNodeViewProps<T>> = ComponentType$1<ReactNodeViewProps<T>>, NodeEditor extends Editor = Editor, Options extends ReactNodeViewRendererOptions = ReactNodeViewRendererOptions> extends NodeView<Component, NodeEditor, Options> {
/**
* The renderer instance.
*/
renderer: ReactRenderer<unknown, ReactNodeViewProps<T>>;
/**
* The element that holds the rich-text content of the node.
*/
contentDOMElement: HTMLElement | null;
/**
* The requestAnimationFrame ID used for selection updates.
*/
selectionRafId: number | null;
/**
* The last known position of this node view, used to detect position-only
* changes that don't produce a new node object reference.
*/
private currentPos;
/**
* Fires on editor updates when trackNodeViewPosition is enabled.
* Detects position shifts where update() is NOT called (e.g. typing above).
*/
private handlePositionUpdate;
constructor(component: Component, props: NodeViewRendererProps, options?: Partial<Options>);
private cachedExtensionWithSyncedStorage;
/**
* 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(): NodeViewRendererProps['extension'];
/**
* Setup the React component.
* Called on initialization.
*/
mount(): void;
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom(): HTMLElement;
/**
* Return the content DOM element.
* This is the element that will be used to display the rich-text content of the node.
*/
get contentDOM(): HTMLElement | null;
/**
* On editor selection update, check if the node is selected.
* If it is, call `selectNode`, otherwise call `deselectNode`.
*/
handleSelectionUpdate(): void;
/**
* On update, update the React component.
* To prevent unnecessary updates, the `update` option can be used.
*/
update(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean;
/**
* Select the node.
* Add the `selected` prop and the `ProseMirror-selectednode` class.
*/
selectNode(): void;
/**
* Deselect the node.
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
*/
deselectNode(): void;
/**
* Destroy the React component instance.
*/
destroy(): void;
/**
* Update the attributes of the top-level element that holds the React component.
* Applying the attributes defined in the `attrs` option.
*/
updateElementAttributes(): void;
declare class ReactNodeView<T = HTMLElement, Component extends ComponentType<ReactNodeViewProps<T>> = ComponentType<ReactNodeViewProps<T>>, NodeEditor extends Editor = Editor, Options extends ReactNodeViewRendererOptions = ReactNodeViewRendererOptions> extends NodeView<Component, NodeEditor, Options> {
/**
* The renderer instance.
*/
renderer: ReactRenderer<unknown, ReactNodeViewProps<T>>;
/**
* The element that holds the rich-text content of the node.
*/
contentDOMElement: HTMLElement | null;
/**
* The requestAnimationFrame ID used for selection updates.
*/
selectionRafId: number | null;
/**
* The last known position of this node view, used to detect position-only
* changes that don't produce a new node object reference.
*/
private currentPos;
/**
* Fires on editor updates when trackNodeViewPosition is enabled.
* Detects position shifts where update() is NOT called (e.g. typing above).
*/
private handlePositionUpdate;
constructor(component: Component, props: NodeViewRendererProps, options?: Partial<Options>);
private cachedExtensionWithSyncedStorage;
/**
* 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(): NodeViewRendererProps['extension'];
/**
* Setup the React component.
* Called on initialization.
*/
mount(): void;
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom(): HTMLElement;
/**
* Return the content DOM element.
* This is the element that will be used to display the rich-text content of the node.
*/
get contentDOM(): HTMLElement | null;
/**
* On editor selection update, check if the node is selected.
* If it is, call `selectNode`, otherwise call `deselectNode`.
*/
handleSelectionUpdate(): void;
/**
* On update, update the React component.
* To prevent unnecessary updates, the `update` option can be used.
*/
update(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean;
/**
* Select the node.
* Add the `selected` prop and the `ProseMirror-selectednode` class.
*/
selectNode(): void;
/**
* Deselect the node.
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
*/
deselectNode(): void;
/**
* Destroy the React component instance.
*/
destroy(): void;
/**
* Update the attributes of the top-level element that holds the React component.
* Applying the attributes defined in the `attrs` option.
*/
updateElementAttributes(): void;
}

@@ -331,4 +339,5 @@ /**

*/
declare function ReactNodeViewRenderer<T = HTMLElement>(component: ComponentType$1<ReactNodeViewProps<T>>, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer;
declare function ReactNodeViewRenderer<T = HTMLElement>(component: ComponentType<ReactNodeViewProps<T>>, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer;
//#endregion
//#region src/ReactWidgetRenderer.d.ts
/**

@@ -339,35 +348,35 @@ * Props every widget-decoration component receives in addition to the props you

interface ReactWidgetDecorationProps {
editor: Editor;
getPos: () => number | undefined;
editor: Editor;
getPos: () => number | undefined;
}
interface ReactWidgetRendererOptions<P extends Record<string, any> = object> extends WidgetDecorationOptions {
/**
* The editor instance.
*/
editor: Editor;
/**
* The document position the widget is rendered at.
*/
pos: number;
/**
* A stable, position-independent identifier for the widget.
* Reusing the same key keeps the component mounted across re-renders.
* Good: `comment-${id}`. Bad: paragraph index or document position.
*/
key: string;
/**
* Props passed to the component (merged with {@link ReactWidgetDecorationProps}).
*/
props?: P;
/**
* The tag name of the wrapper element. Defaults to `'span'` (widgets are
* usually inline). Applied only when the renderer is first created; later
* renders with the same `key` keep the original tag.
*/
as?: string;
/**
* Applied only when the renderer is first created; later renders with the
* same `key` keep the original class.
*/
className?: string;
/**
* The editor instance.
*/
editor: Editor;
/**
* The document position the widget is rendered at.
*/
pos: number;
/**
* A stable, position-independent identifier for the widget.
* Reusing the same key keeps the component mounted across re-renders.
* Good: `comment-${id}`. Bad: paragraph index or document position.
*/
key: string;
/**
* Props passed to the component (merged with {@link ReactWidgetDecorationProps}).
*/
props?: P;
/**
* The tag name of the wrapper element. Defaults to `'span'` (widgets are
* usually inline). Applied only when the renderer is first created; later
* renders with the same `key` keep the original tag.
*/
as?: string;
/**
* Applied only when the renderer is first created; later renders with the
* same `key` keep the original class.
*/
className?: string;
}

@@ -392,4 +401,5 @@ /**

*/
declare function ReactWidgetRenderer<P extends Record<string, any> = object>(component: ComponentType$1<P & ReactWidgetDecorationProps>, options: ReactWidgetRendererOptions<P>): WidgetDecoration;
declare function ReactWidgetRenderer<P extends Record<string, any> = object>(component: ComponentType<P & ReactWidgetDecorationProps>, options: ReactWidgetRendererOptions<P>): WidgetDecoration;
//#endregion
//#region src/Tiptap.d.ts
/**

@@ -403,4 +413,4 @@ * The shape of the React context used by the `<Tiptap />` components.

type TiptapContextType = {
/** The Tiptap editor instance. */
editor: Editor;
/** The Tiptap editor instance. */
editor: index_d_exports.Editor;
};

@@ -412,3 +422,3 @@ /**

*/
declare const TiptapContext: React.Context<TiptapContextType>;
declare const TiptapContext: import("react").Context<TiptapContextType>;
/**

@@ -463,14 +473,14 @@ * Hook to read the Tiptap context and access the editor instance.

*/
declare function useTiptapState<TSelectorResult>(selector: (context: EditorStateSnapshot<Editor>) => TSelectorResult, equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean): TSelectorResult;
declare function useTiptapState<TSelectorResult>(selector: (context: EditorStateSnapshot<index_d_exports.Editor>) => TSelectorResult, equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean): TSelectorResult;
type TiptapWrapperEditorInstanceProps = {
/**
* The editor instance to provide to child components.
* Use `useEditor()` to create this instance.
*/
editor: Editor;
/**
* The editor instance to provide to child components.
* Use `useEditor()` to create this instance.
*/
editor: index_d_exports.Editor;
} | {
/**
* @deprecated Use `editor` instead. Will be removed in the next major version.
*/
instance: Editor;
/**
* @deprecated Use `editor` instead. Will be removed in the next major version.
*/
instance: index_d_exports.Editor;
};

@@ -481,3 +491,3 @@ /**

type TiptapWrapperProps = TiptapWrapperEditorInstanceProps & {
children: ReactNode;
children: ReactNode;
};

@@ -511,5 +521,5 @@ /**

*/
declare function TiptapWrapper({ children, ...props }: TiptapWrapperProps): react_jsx_runtime.JSX.Element;
declare function TiptapWrapper({ children, ...props }: TiptapWrapperProps): import("react").JSX.Element;
declare namespace TiptapWrapper {
var displayName: string;
var displayName: string;
}

@@ -529,5 +539,5 @@ /**

*/
declare function TiptapContent({ ...rest }: Omit<EditorContentProps, 'editor' | 'ref'>): react_jsx_runtime.JSX.Element;
declare function TiptapContent({ ...rest }: Omit<EditorContentProps, 'editor' | 'ref'>): import("react").JSX.Element;
declare namespace TiptapContent {
var displayName: string;
var displayName: string;
}

@@ -567,27 +577,28 @@ /**

declare const Tiptap: typeof TiptapWrapper & {
/**
* The Tiptap Content component that renders the EditorContent with the editor instance from the context.
* @see TiptapContent
*/
Content: typeof TiptapContent;
/**
* The Tiptap Content component that renders the EditorContent with the editor instance from the context.
* @see TiptapContent
*/
Content: typeof TiptapContent;
};
//#endregion
//#region src/useEditorState.d.ts
type EditorStateSnapshot<TEditor extends Editor | null = Editor | null> = {
editor: TEditor;
transactionNumber: number;
editor: TEditor;
transactionNumber: number;
};
type UseEditorStateOptions<TSelectorResult, TEditor extends Editor | null = Editor | null> = {
/**
* The editor instance.
*/
editor: TEditor;
/**
* A selector function to determine the value to compare for re-rendering.
*/
selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult;
/**
* A custom equality function to determine if the editor should re-render.
* @default `deepEqual` from `fast-deep-equal`
*/
equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean;
/**
* The editor instance.
*/
editor: TEditor;
/**
* A selector function to determine the value to compare for re-rendering.
*/
selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult;
/**
* A custom equality function to determine if the editor should re-render.
* @default `deepEqual` from `fast-deep-equal`
*/
equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean;
};

@@ -618,19 +629,25 @@ /**

declare function useEditorState<TSelectorResult>(options: UseEditorStateOptions<TSelectorResult, Editor | null>): TSelectorResult | null;
//#endregion
//#region src/useReactNodeView.d.ts
interface ReactNodeViewContextProps {
onDragStart?: (event: DragEvent) => void;
nodeViewContentRef?: (element: HTMLElement | null) => void;
/**
* This allows you to add children into the NodeViewContent component.
* This is useful when statically rendering the content of a node view.
*/
nodeViewContentChildren?: ReactNode;
onDragStart?: (event: DragEvent) => void;
nodeViewContentRef?: (element: HTMLElement | null) => void;
/**
* This allows you to add children into the NodeViewContent component.
* This is useful when statically rendering the content of a node view.
*/
nodeViewContentChildren?: ReactNode;
}
declare const ReactNodeViewContext: React.Context<ReactNodeViewContextProps>;
declare const ReactNodeViewContentProvider: ({ children, content, }: {
children: ReactNode;
content: ReactNode;
}) => React.FunctionComponentElement<React.ProviderProps<ReactNodeViewContextProps>>;
declare const ReactNodeViewContext: import("react").Context<ReactNodeViewContextProps>;
declare const ReactNodeViewContentProvider: ({ children, content }: {
children: ReactNode;
content: ReactNode;
}) => import("react").FunctionComponentElement<import("react").ProviderProps<ReactNodeViewContextProps>>;
declare const useReactNodeView: () => ReactNodeViewContextProps;
export { EditorConsumer, EditorContent, type EditorContentProps, EditorContext, type EditorContextValue, EditorProvider, type EditorProviderProps, type EditorStateSnapshot, MarkViewContent, type MarkViewContentProps, type MarkViewContextProps, NodeViewContent, type NodeViewContentProps, NodeViewWrapper, type NodeViewWrapperProps, PureEditorContent, ReactMarkView, ReactMarkViewContext, ReactMarkViewRenderer, type ReactMarkViewRendererOptions, ReactNodeView, ReactNodeViewContentProvider, ReactNodeViewContext, type ReactNodeViewContextProps, type ReactNodeViewProps, ReactNodeViewRenderer, type ReactNodeViewRendererOptions, ReactRenderer, type ReactRendererOptions, type ReactWidgetDecorationProps, ReactWidgetRenderer, type ReactWidgetRendererOptions, Tiptap, TiptapContent, TiptapContext, type TiptapContextType, TiptapWrapper, type TiptapWrapperEditorInstanceProps, type TiptapWrapperProps, type UseEditorOptions, type UseEditorStateOptions, createContentComponent, useCurrentEditor, useEditor, useEditorState, useReactNodeView, useTiptap, useTiptapState };
declare namespace index_d_exports {
export { EditorConsumer, EditorContent, EditorContentProps, EditorContext, EditorContextValue, EditorProvider, EditorProviderProps, EditorStateSnapshot, MarkViewContent, MarkViewContentProps, MarkViewContextProps, NodeViewContent, NodeViewContentProps, NodeViewWrapper, NodeViewWrapperProps, PureEditorContent, ReactMarkView, ReactMarkViewContext, ReactMarkViewRenderer, ReactMarkViewRendererOptions, ReactNodeView, ReactNodeViewContentProvider, ReactNodeViewContext, ReactNodeViewContextProps, ReactNodeViewProps, ReactNodeViewRenderer, ReactNodeViewRendererOptions, ReactRenderer, ReactRendererOptions, ReactWidgetDecorationProps, ReactWidgetRenderer, ReactWidgetRendererOptions, Tiptap, TiptapContent, TiptapContext, TiptapContextType, TiptapWrapper, TiptapWrapperEditorInstanceProps, TiptapWrapperProps, UseEditorOptions, UseEditorStateOptions, createContentComponent, useCurrentEditor, useEditor, useEditorState, useReactNodeView, useTiptap, useTiptapState };
}
import * as import__tiptap_core from "@tiptap/core";
//#endregion
export { EditorConsumer, EditorContent, EditorContentProps, EditorContext, EditorContextValue, EditorProvider, EditorProviderProps, EditorStateSnapshot, MarkViewContent, MarkViewContentProps, MarkViewContextProps, NodeViewContent, NodeViewContentProps, NodeViewWrapper, NodeViewWrapperProps, PureEditorContent, ReactMarkView, ReactMarkViewContext, ReactMarkViewRenderer, ReactMarkViewRendererOptions, ReactNodeView, ReactNodeViewContentProvider, ReactNodeViewContext, ReactNodeViewContextProps, ReactNodeViewProps, ReactNodeViewRenderer, ReactNodeViewRendererOptions, ReactRenderer, ReactRendererOptions, ReactWidgetDecorationProps, ReactWidgetRenderer, ReactWidgetRendererOptions, Tiptap, TiptapContent, TiptapContext, TiptapContextType, TiptapWrapper, TiptapWrapperEditorInstanceProps, TiptapWrapperProps, UseEditorOptions, UseEditorStateOptions, createContentComponent, useCurrentEditor, useEditor, useEditorState, useReactNodeView, useTiptap, useTiptapState };
//# sourceMappingURL=index.d.cts.map

@@ -1,9 +0,8 @@

import * as react_jsx_runtime from 'react/jsx-runtime';
import { EditorOptions, Editor, MarkView, MarkViewProps, MarkViewRendererOptions, MarkViewRenderer, NodeViewProps, NodeViewRendererOptions, NodeView, NodeViewRendererProps, NodeViewRenderer, WidgetDecorationOptions, WidgetDecoration } from '@tiptap/core';
export * from '@tiptap/core';
import * as React from 'react';
import React__default, { DependencyList, ReactNode, HTMLAttributes, ComponentClass, FunctionComponent, ForwardRefExoticComponent, PropsWithoutRef, RefAttributes, ReactPortal, HTMLProps, ForwardedRef, ComponentProps, ComponentType as ComponentType$1 } from 'react';
import { Node } from '@tiptap/pm/model';
import { Decoration, DecorationSource } from '@tiptap/pm/view';
import React, { ComponentClass, ComponentProps, ComponentType, DependencyList, ForwardRefExoticComponent, ForwardedRef, FunctionComponent, HTMLAttributes, HTMLProps, PropsWithoutRef, ReactNode, ReactPortal, RefAttributes } from "react";
import { Editor, EditorOptions, MarkView, MarkViewProps, MarkViewRenderer, MarkViewRendererOptions, NodeView, NodeViewProps, NodeViewRenderer, NodeViewRendererOptions, NodeViewRendererProps, WidgetDecoration, WidgetDecorationOptions } from "@tiptap/core";
import { Node } from "@tiptap/pm/model";
import { Decoration, DecorationSource } from "@tiptap/pm/view";
export * from "@tiptap/core";
//#endregion
//#region src/useEditor.d.ts
/**

@@ -13,15 +12,15 @@ * The options for the `useEditor` hook.

type UseEditorOptions = Partial<EditorOptions> & {
/**
* Whether to render the editor on the first render.
* If client-side rendering, set this to `true`.
* If server-side rendering, set this to `false`.
* @default true
*/
immediatelyRender?: boolean;
/**
* Whether to re-render the editor on each transaction.
* This is legacy behavior that will be removed in future versions.
* @default false
*/
shouldRerenderOnTransaction?: boolean;
/**
* Whether to render the editor on the first render.
* If client-side rendering, set this to `true`.
* If server-side rendering, set this to `false`.
* @default true
*/
immediatelyRender?: boolean;
/**
* Whether to re-render the editor on each transaction.
* This is legacy behavior that will be removed in future versions.
* @default false
*/
shouldRerenderOnTransaction?: boolean;
};

@@ -36,3 +35,3 @@ /**

declare function useEditor(options: UseEditorOptions & {
immediatelyRender: false;
immediatelyRender: false;
}, deps?: DependencyList): Editor | null;

@@ -47,8 +46,9 @@ /**

declare function useEditor(options: UseEditorOptions, deps?: DependencyList): Editor;
//#endregion
//#region src/Context.d.ts
type EditorContextValue = {
editor: Editor | null;
editor: Editor | null;
};
declare const EditorContext: React__default.Context<EditorContextValue>;
declare const EditorConsumer: React__default.Consumer<EditorContextValue>;
declare const EditorContext: React.Context<EditorContextValue>;
declare const EditorConsumer: React.Consumer<EditorContextValue>;
/**

@@ -59,6 +59,6 @@ * A hook to get the current editor instance.

type EditorProviderProps = {
children?: ReactNode;
slotBefore?: ReactNode;
slotAfter?: ReactNode;
editorContainerProps?: HTMLAttributes<HTMLDivElement>;
children?: ReactNode;
slotBefore?: ReactNode;
slotAfter?: ReactNode;
editorContainerProps?: HTMLAttributes<HTMLDivElement>;
} & UseEditorOptions;

@@ -70,31 +70,32 @@ /**

*/
declare function EditorProvider({ children, slotAfter, slotBefore, editorContainerProps, ...editorOptions }: EditorProviderProps): react_jsx_runtime.JSX.Element | null;
declare function EditorProvider({ children, slotAfter, slotBefore, editorContainerProps, ...editorOptions }: EditorProviderProps): React.JSX.Element | null;
//#endregion
//#region src/ReactRenderer.d.ts
interface ReactRendererOptions {
/**
* The editor instance.
* @type {Editor}
*/
editor: Editor;
/**
* The props for the component.
* @type {Record<string, any>}
* @default {}
*/
props?: Record<string, any>;
/**
* The tag name of the element.
* @type {string}
* @default 'div'
*/
as?: string;
/**
* The class name of the element.
* @type {string}
* @default ''
* @example 'foo bar'
*/
className?: string;
/**
* The editor instance.
* @type {Editor}
*/
editor: Editor;
/**
* The props for the component.
* @type {Record<string, any>}
* @default {}
*/
props?: Record<string, any>;
/**
* The tag name of the element.
* @type {string}
* @default 'div'
*/
as?: string;
/**
* The class name of the element.
* @type {string}
* @default ''
* @example 'foo bar'
*/
className?: string;
}
type ComponentType<R, P> = ComponentClass<P> | FunctionComponent<P> | ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<R>>;
type ComponentType$1<R, P> = ComponentClass<P> | FunctionComponent<P> | ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<R>>;
/**

@@ -112,216 +113,223 @@ * The ReactRenderer class. It's responsible for rendering React components inside the editor.

declare class ReactRenderer<R = unknown, P extends Record<string, any> = object> {
id: string;
editor: EditorWithContentComponent;
component: any;
element: HTMLElement;
props: P;
reactElement: ReactNode;
ref: R | null;
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
destroyed: boolean;
/**
* Immediately creates element and renders the provided React component.
*/
constructor(component: ComponentType<R, P>, { editor, props, as, className }: ReactRendererOptions);
/**
* Render the React component.
*/
render(): void;
/**
* Re-renders the React component with new props.
* Skips the render if none of the supplied props actually changed,
* to avoid unnecessary portal re-creation.
*/
updateProps(props?: Record<string, any>): void;
/**
* Destroy the React component.
*/
destroy(): void;
/**
* Update the attributes of the element that holds the React component.
*/
updateAttributes(attributes: Record<string, string>): void;
id: string;
editor: EditorWithContentComponent;
component: any;
element: HTMLElement;
props: P;
reactElement: ReactNode;
ref: R | null;
/**
* Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
*/
destroyed: boolean;
/**
* Immediately creates element and renders the provided React component.
*/
constructor(component: ComponentType$1<R, P>, { editor, props, as, className }: ReactRendererOptions);
/**
* Render the React component.
*/
render(): void;
/**
* Re-renders the React component with new props.
* Skips the render if none of the supplied props actually changed,
* to avoid unnecessary portal re-creation.
*/
updateProps(props?: Record<string, any>): void;
/**
* Destroy the React component.
*/
destroy(): void;
/**
* Update the attributes of the element that holds the React component.
*/
updateAttributes(attributes: Record<string, string>): void;
}
//#endregion
//#region src/Editor.d.ts
type EditorWithContentComponent = Editor & {
contentComponent?: ContentComponent | null;
isEditorContentInitialized?: boolean;
contentComponent?: ContentComponent | null;
isEditorContentInitialized?: boolean;
};
type ContentComponent = {
setRenderer(id: string, renderer: ReactRenderer): void;
removeRenderer(id: string): void;
subscribe: (callback: () => void) => () => void;
getSnapshot: () => Record<string, ReactPortal>;
getServerSnapshot: () => Record<string, ReactPortal>;
setRenderer(id: string, renderer: ReactRenderer): void;
removeRenderer(id: string): void;
subscribe: (callback: () => void) => () => void;
getSnapshot: () => Record<string, ReactPortal>;
getServerSnapshot: () => Record<string, ReactPortal>;
};
//#endregion
//#region src/EditorContent.d.ts
interface EditorContentProps extends HTMLProps<HTMLDivElement> {
editor: Editor | null;
innerRef?: ForwardedRef<HTMLDivElement | null>;
editor: Editor | null;
innerRef?: ForwardedRef<HTMLDivElement | null>;
}
declare function createContentComponent(): ContentComponent;
declare class PureEditorContent extends React__default.Component<EditorContentProps, {
hasContentComponentInitialized: boolean;
declare class PureEditorContent extends React.Component<EditorContentProps, {
hasContentComponentInitialized: boolean;
}> {
editorContentRef: React__default.RefObject<any>;
constructor(props: EditorContentProps);
componentDidMount(): void;
componentDidUpdate(): void;
init(): void;
componentWillUnmount(): void;
render(): react_jsx_runtime.JSX.Element;
editorContentRef: React.RefObject<any>;
constructor(props: EditorContentProps);
componentDidMount(): void;
componentDidUpdate(): void;
init(): void;
componentWillUnmount(): void;
render(): React.JSX.Element;
}
declare const EditorContent: React__default.NamedExoticComponent<Omit<EditorContentProps, "ref"> & React__default.RefAttributes<HTMLDivElement>>;
type NodeViewContentProps<T extends keyof React__default.JSX.IntrinsicElements = 'div'> = {
as?: NoInfer<T>;
declare const EditorContent: React.NamedExoticComponent<Omit<EditorContentProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
//#endregion
//#region src/NodeViewContent.d.ts
type NodeViewContentProps<T extends keyof React.JSX.IntrinsicElements = 'div'> = {
as?: NoInfer<T>;
} & ComponentProps<T>;
declare function NodeViewContent<T extends keyof React__default.JSX.IntrinsicElements = 'div'>({ as: Tag, ...props }: NodeViewContentProps<T>): react_jsx_runtime.JSX.Element;
declare function NodeViewContent<T extends keyof React.JSX.IntrinsicElements = 'div'>({ as: Tag, ...props }: NodeViewContentProps<T>): React.JSX.Element;
//#endregion
//#region src/NodeViewWrapper.d.ts
interface NodeViewWrapperProps {
[key: string]: any;
as?: React__default.ElementType;
[key: string]: any;
as?: React.ElementType;
}
declare const NodeViewWrapper: React__default.FC<NodeViewWrapperProps>;
declare const NodeViewWrapper: React.FC<NodeViewWrapperProps>;
//#endregion
//#region src/ReactMarkViewRenderer.d.ts
interface MarkViewContextProps {
markViewContentRef: (element: HTMLElement | null) => void;
markViewContentRef: (element: HTMLElement | null) => void;
}
declare const ReactMarkViewContext: React__default.Context<MarkViewContextProps>;
type MarkViewContentProps<T extends keyof React__default.JSX.IntrinsicElements = 'span'> = {
as?: T;
} & Omit<React__default.ComponentProps<T>, 'as'>;
declare const MarkViewContent: <T extends keyof React__default.JSX.IntrinsicElements = "span">(props: MarkViewContentProps<T>) => react_jsx_runtime.JSX.Element;
declare const ReactMarkViewContext: React.Context<MarkViewContextProps>;
type MarkViewContentProps<T extends keyof React.JSX.IntrinsicElements = 'span'> = {
as?: T;
} & Omit<React.ComponentProps<T>, 'as'>;
declare const MarkViewContent: <T extends keyof React.JSX.IntrinsicElements = "span">(props: MarkViewContentProps<T>) => React.JSX.Element;
interface ReactMarkViewRendererOptions extends MarkViewRendererOptions {
/**
* The tag name of the element wrapping the React component.
*/
as?: string;
className?: string;
attrs?: {
[key: string]: string;
};
/**
* The tag name of the element wrapping the React component.
*/
as?: string;
className?: string;
attrs?: {
[key: string]: string;
};
}
declare class ReactMarkView extends MarkView<React__default.ComponentType<MarkViewProps>, ReactMarkViewRendererOptions> {
renderer: ReactRenderer;
contentDOMElement: HTMLElement;
constructor(component: React__default.ComponentType<MarkViewProps>, props: MarkViewProps, options?: Partial<ReactMarkViewRendererOptions>);
get dom(): HTMLElement;
get contentDOM(): HTMLElement;
declare class ReactMarkView extends MarkView<React.ComponentType<MarkViewProps>, ReactMarkViewRendererOptions> {
renderer: ReactRenderer;
contentDOMElement: HTMLElement;
constructor(component: React.ComponentType<MarkViewProps>, props: MarkViewProps, options?: Partial<ReactMarkViewRendererOptions>);
get dom(): HTMLElement;
get contentDOM(): HTMLElement;
}
declare function ReactMarkViewRenderer(component: React__default.ComponentType<MarkViewProps>, options?: Partial<ReactMarkViewRendererOptions>): MarkViewRenderer;
declare function ReactMarkViewRenderer(component: React.ComponentType<MarkViewProps>, options?: Partial<ReactMarkViewRendererOptions>): MarkViewRenderer;
//#endregion
//#region src/types.d.ts
type ReactNodeViewProps<T = HTMLElement> = NodeViewProps & {
ref: React__default.RefObject<T | null>;
ref: React.RefObject<T | null>;
};
//#endregion
//#region src/ReactNodeViewRenderer.d.ts
interface ReactNodeViewRendererOptions extends NodeViewRendererOptions {
/**
* This function is called when the node view is updated.
* It allows you to compare the old node with the new node and decide if the component should update.
*/
update: ((props: {
oldNode: Node;
oldDecorations: readonly Decoration[];
oldInnerDecorations: DecorationSource;
newNode: Node;
newDecorations: readonly Decoration[];
innerDecorations: DecorationSource;
updateProps: () => void;
}) => boolean) | null;
/**
* The tag name of the element wrapping the React component.
*/
as?: string;
/**
* The class name of the element wrapping the React component.
*/
className?: string;
/**
* Attributes that should be applied to the element wrapping the React component.
* If this is a function, it will be called each time the node view is updated.
* If this is an object, it will be applied once when the node view is mounted.
*/
attrs?: Record<string, string> | ((props: {
node: Node;
HTMLAttributes: Record<string, any>;
}) => Record<string, string>);
/**
* This function is called when the node view is updated.
* It allows you to compare the old node with the new node and decide if the component should update.
*/
update: ((props: {
oldNode: Node;
oldDecorations: readonly Decoration[];
oldInnerDecorations: DecorationSource;
newNode: Node;
newDecorations: readonly Decoration[];
innerDecorations: DecorationSource;
updateProps: () => void;
}) => boolean) | null;
/**
* The tag name of the element wrapping the React component.
*/
as?: string;
/**
* The class name of the element wrapping the React component.
*/
className?: string;
/**
* Attributes that should be applied to the element wrapping the React component.
* If this is a function, it will be called each time the node view is updated.
* If this is an object, it will be applied once when the node view is mounted.
*/
attrs?: Record<string, string> | ((props: {
node: Node;
HTMLAttributes: Record<string, any>;
}) => Record<string, string>);
}
declare class ReactNodeView<T = HTMLElement, Component extends ComponentType$1<ReactNodeViewProps<T>> = ComponentType$1<ReactNodeViewProps<T>>, NodeEditor extends Editor = Editor, Options extends ReactNodeViewRendererOptions = ReactNodeViewRendererOptions> extends NodeView<Component, NodeEditor, Options> {
/**
* The renderer instance.
*/
renderer: ReactRenderer<unknown, ReactNodeViewProps<T>>;
/**
* The element that holds the rich-text content of the node.
*/
contentDOMElement: HTMLElement | null;
/**
* The requestAnimationFrame ID used for selection updates.
*/
selectionRafId: number | null;
/**
* The last known position of this node view, used to detect position-only
* changes that don't produce a new node object reference.
*/
private currentPos;
/**
* Fires on editor updates when trackNodeViewPosition is enabled.
* Detects position shifts where update() is NOT called (e.g. typing above).
*/
private handlePositionUpdate;
constructor(component: Component, props: NodeViewRendererProps, options?: Partial<Options>);
private cachedExtensionWithSyncedStorage;
/**
* 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(): NodeViewRendererProps['extension'];
/**
* Setup the React component.
* Called on initialization.
*/
mount(): void;
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom(): HTMLElement;
/**
* Return the content DOM element.
* This is the element that will be used to display the rich-text content of the node.
*/
get contentDOM(): HTMLElement | null;
/**
* On editor selection update, check if the node is selected.
* If it is, call `selectNode`, otherwise call `deselectNode`.
*/
handleSelectionUpdate(): void;
/**
* On update, update the React component.
* To prevent unnecessary updates, the `update` option can be used.
*/
update(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean;
/**
* Select the node.
* Add the `selected` prop and the `ProseMirror-selectednode` class.
*/
selectNode(): void;
/**
* Deselect the node.
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
*/
deselectNode(): void;
/**
* Destroy the React component instance.
*/
destroy(): void;
/**
* Update the attributes of the top-level element that holds the React component.
* Applying the attributes defined in the `attrs` option.
*/
updateElementAttributes(): void;
declare class ReactNodeView<T = HTMLElement, Component extends ComponentType<ReactNodeViewProps<T>> = ComponentType<ReactNodeViewProps<T>>, NodeEditor extends Editor = Editor, Options extends ReactNodeViewRendererOptions = ReactNodeViewRendererOptions> extends NodeView<Component, NodeEditor, Options> {
/**
* The renderer instance.
*/
renderer: ReactRenderer<unknown, ReactNodeViewProps<T>>;
/**
* The element that holds the rich-text content of the node.
*/
contentDOMElement: HTMLElement | null;
/**
* The requestAnimationFrame ID used for selection updates.
*/
selectionRafId: number | null;
/**
* The last known position of this node view, used to detect position-only
* changes that don't produce a new node object reference.
*/
private currentPos;
/**
* Fires on editor updates when trackNodeViewPosition is enabled.
* Detects position shifts where update() is NOT called (e.g. typing above).
*/
private handlePositionUpdate;
constructor(component: Component, props: NodeViewRendererProps, options?: Partial<Options>);
private cachedExtensionWithSyncedStorage;
/**
* 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(): NodeViewRendererProps['extension'];
/**
* Setup the React component.
* Called on initialization.
*/
mount(): void;
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom(): HTMLElement;
/**
* Return the content DOM element.
* This is the element that will be used to display the rich-text content of the node.
*/
get contentDOM(): HTMLElement | null;
/**
* On editor selection update, check if the node is selected.
* If it is, call `selectNode`, otherwise call `deselectNode`.
*/
handleSelectionUpdate(): void;
/**
* On update, update the React component.
* To prevent unnecessary updates, the `update` option can be used.
*/
update(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean;
/**
* Select the node.
* Add the `selected` prop and the `ProseMirror-selectednode` class.
*/
selectNode(): void;
/**
* Deselect the node.
* Remove the `selected` prop and the `ProseMirror-selectednode` class.
*/
deselectNode(): void;
/**
* Destroy the React component instance.
*/
destroy(): void;
/**
* Update the attributes of the top-level element that holds the React component.
* Applying the attributes defined in the `attrs` option.
*/
updateElementAttributes(): void;
}

@@ -331,4 +339,5 @@ /**

*/
declare function ReactNodeViewRenderer<T = HTMLElement>(component: ComponentType$1<ReactNodeViewProps<T>>, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer;
declare function ReactNodeViewRenderer<T = HTMLElement>(component: ComponentType<ReactNodeViewProps<T>>, options?: Partial<ReactNodeViewRendererOptions>): NodeViewRenderer;
//#endregion
//#region src/ReactWidgetRenderer.d.ts
/**

@@ -339,35 +348,35 @@ * Props every widget-decoration component receives in addition to the props you

interface ReactWidgetDecorationProps {
editor: Editor;
getPos: () => number | undefined;
editor: Editor;
getPos: () => number | undefined;
}
interface ReactWidgetRendererOptions<P extends Record<string, any> = object> extends WidgetDecorationOptions {
/**
* The editor instance.
*/
editor: Editor;
/**
* The document position the widget is rendered at.
*/
pos: number;
/**
* A stable, position-independent identifier for the widget.
* Reusing the same key keeps the component mounted across re-renders.
* Good: `comment-${id}`. Bad: paragraph index or document position.
*/
key: string;
/**
* Props passed to the component (merged with {@link ReactWidgetDecorationProps}).
*/
props?: P;
/**
* The tag name of the wrapper element. Defaults to `'span'` (widgets are
* usually inline). Applied only when the renderer is first created; later
* renders with the same `key` keep the original tag.
*/
as?: string;
/**
* Applied only when the renderer is first created; later renders with the
* same `key` keep the original class.
*/
className?: string;
/**
* The editor instance.
*/
editor: Editor;
/**
* The document position the widget is rendered at.
*/
pos: number;
/**
* A stable, position-independent identifier for the widget.
* Reusing the same key keeps the component mounted across re-renders.
* Good: `comment-${id}`. Bad: paragraph index or document position.
*/
key: string;
/**
* Props passed to the component (merged with {@link ReactWidgetDecorationProps}).
*/
props?: P;
/**
* The tag name of the wrapper element. Defaults to `'span'` (widgets are
* usually inline). Applied only when the renderer is first created; later
* renders with the same `key` keep the original tag.
*/
as?: string;
/**
* Applied only when the renderer is first created; later renders with the
* same `key` keep the original class.
*/
className?: string;
}

@@ -392,4 +401,5 @@ /**

*/
declare function ReactWidgetRenderer<P extends Record<string, any> = object>(component: ComponentType$1<P & ReactWidgetDecorationProps>, options: ReactWidgetRendererOptions<P>): WidgetDecoration;
declare function ReactWidgetRenderer<P extends Record<string, any> = object>(component: ComponentType<P & ReactWidgetDecorationProps>, options: ReactWidgetRendererOptions<P>): WidgetDecoration;
//#endregion
//#region src/Tiptap.d.ts
/**

@@ -403,4 +413,4 @@ * The shape of the React context used by the `<Tiptap />` components.

type TiptapContextType = {
/** The Tiptap editor instance. */
editor: Editor;
/** The Tiptap editor instance. */
editor: index_d_exports.Editor;
};

@@ -412,3 +422,3 @@ /**

*/
declare const TiptapContext: React.Context<TiptapContextType>;
declare const TiptapContext: import("react").Context<TiptapContextType>;
/**

@@ -463,14 +473,14 @@ * Hook to read the Tiptap context and access the editor instance.

*/
declare function useTiptapState<TSelectorResult>(selector: (context: EditorStateSnapshot<Editor>) => TSelectorResult, equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean): TSelectorResult;
declare function useTiptapState<TSelectorResult>(selector: (context: EditorStateSnapshot<index_d_exports.Editor>) => TSelectorResult, equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean): TSelectorResult;
type TiptapWrapperEditorInstanceProps = {
/**
* The editor instance to provide to child components.
* Use `useEditor()` to create this instance.
*/
editor: Editor;
/**
* The editor instance to provide to child components.
* Use `useEditor()` to create this instance.
*/
editor: index_d_exports.Editor;
} | {
/**
* @deprecated Use `editor` instead. Will be removed in the next major version.
*/
instance: Editor;
/**
* @deprecated Use `editor` instead. Will be removed in the next major version.
*/
instance: index_d_exports.Editor;
};

@@ -481,3 +491,3 @@ /**

type TiptapWrapperProps = TiptapWrapperEditorInstanceProps & {
children: ReactNode;
children: ReactNode;
};

@@ -511,5 +521,5 @@ /**

*/
declare function TiptapWrapper({ children, ...props }: TiptapWrapperProps): react_jsx_runtime.JSX.Element;
declare function TiptapWrapper({ children, ...props }: TiptapWrapperProps): import("react").JSX.Element;
declare namespace TiptapWrapper {
var displayName: string;
var displayName: string;
}

@@ -529,5 +539,5 @@ /**

*/
declare function TiptapContent({ ...rest }: Omit<EditorContentProps, 'editor' | 'ref'>): react_jsx_runtime.JSX.Element;
declare function TiptapContent({ ...rest }: Omit<EditorContentProps, 'editor' | 'ref'>): import("react").JSX.Element;
declare namespace TiptapContent {
var displayName: string;
var displayName: string;
}

@@ -567,27 +577,28 @@ /**

declare const Tiptap: typeof TiptapWrapper & {
/**
* The Tiptap Content component that renders the EditorContent with the editor instance from the context.
* @see TiptapContent
*/
Content: typeof TiptapContent;
/**
* The Tiptap Content component that renders the EditorContent with the editor instance from the context.
* @see TiptapContent
*/
Content: typeof TiptapContent;
};
//#endregion
//#region src/useEditorState.d.ts
type EditorStateSnapshot<TEditor extends Editor | null = Editor | null> = {
editor: TEditor;
transactionNumber: number;
editor: TEditor;
transactionNumber: number;
};
type UseEditorStateOptions<TSelectorResult, TEditor extends Editor | null = Editor | null> = {
/**
* The editor instance.
*/
editor: TEditor;
/**
* A selector function to determine the value to compare for re-rendering.
*/
selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult;
/**
* A custom equality function to determine if the editor should re-render.
* @default `deepEqual` from `fast-deep-equal`
*/
equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean;
/**
* The editor instance.
*/
editor: TEditor;
/**
* A selector function to determine the value to compare for re-rendering.
*/
selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult;
/**
* A custom equality function to determine if the editor should re-render.
* @default `deepEqual` from `fast-deep-equal`
*/
equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean;
};

@@ -618,19 +629,25 @@ /**

declare function useEditorState<TSelectorResult>(options: UseEditorStateOptions<TSelectorResult, Editor | null>): TSelectorResult | null;
//#endregion
//#region src/useReactNodeView.d.ts
interface ReactNodeViewContextProps {
onDragStart?: (event: DragEvent) => void;
nodeViewContentRef?: (element: HTMLElement | null) => void;
/**
* This allows you to add children into the NodeViewContent component.
* This is useful when statically rendering the content of a node view.
*/
nodeViewContentChildren?: ReactNode;
onDragStart?: (event: DragEvent) => void;
nodeViewContentRef?: (element: HTMLElement | null) => void;
/**
* This allows you to add children into the NodeViewContent component.
* This is useful when statically rendering the content of a node view.
*/
nodeViewContentChildren?: ReactNode;
}
declare const ReactNodeViewContext: React.Context<ReactNodeViewContextProps>;
declare const ReactNodeViewContentProvider: ({ children, content, }: {
children: ReactNode;
content: ReactNode;
}) => React.FunctionComponentElement<React.ProviderProps<ReactNodeViewContextProps>>;
declare const ReactNodeViewContext: import("react").Context<ReactNodeViewContextProps>;
declare const ReactNodeViewContentProvider: ({ children, content }: {
children: ReactNode;
content: ReactNode;
}) => import("react").FunctionComponentElement<import("react").ProviderProps<ReactNodeViewContextProps>>;
declare const useReactNodeView: () => ReactNodeViewContextProps;
export { EditorConsumer, EditorContent, type EditorContentProps, EditorContext, type EditorContextValue, EditorProvider, type EditorProviderProps, type EditorStateSnapshot, MarkViewContent, type MarkViewContentProps, type MarkViewContextProps, NodeViewContent, type NodeViewContentProps, NodeViewWrapper, type NodeViewWrapperProps, PureEditorContent, ReactMarkView, ReactMarkViewContext, ReactMarkViewRenderer, type ReactMarkViewRendererOptions, ReactNodeView, ReactNodeViewContentProvider, ReactNodeViewContext, type ReactNodeViewContextProps, type ReactNodeViewProps, ReactNodeViewRenderer, type ReactNodeViewRendererOptions, ReactRenderer, type ReactRendererOptions, type ReactWidgetDecorationProps, ReactWidgetRenderer, type ReactWidgetRendererOptions, Tiptap, TiptapContent, TiptapContext, type TiptapContextType, TiptapWrapper, type TiptapWrapperEditorInstanceProps, type TiptapWrapperProps, type UseEditorOptions, type UseEditorStateOptions, createContentComponent, useCurrentEditor, useEditor, useEditorState, useReactNodeView, useTiptap, useTiptapState };
declare namespace index_d_exports {
export { EditorConsumer, EditorContent, EditorContentProps, EditorContext, EditorContextValue, EditorProvider, EditorProviderProps, EditorStateSnapshot, MarkViewContent, MarkViewContentProps, MarkViewContextProps, NodeViewContent, NodeViewContentProps, NodeViewWrapper, NodeViewWrapperProps, PureEditorContent, ReactMarkView, ReactMarkViewContext, ReactMarkViewRenderer, ReactMarkViewRendererOptions, ReactNodeView, ReactNodeViewContentProvider, ReactNodeViewContext, ReactNodeViewContextProps, ReactNodeViewProps, ReactNodeViewRenderer, ReactNodeViewRendererOptions, ReactRenderer, ReactRendererOptions, ReactWidgetDecorationProps, ReactWidgetRenderer, ReactWidgetRendererOptions, Tiptap, TiptapContent, TiptapContext, TiptapContextType, TiptapWrapper, TiptapWrapperEditorInstanceProps, TiptapWrapperProps, UseEditorOptions, UseEditorStateOptions, createContentComponent, useCurrentEditor, useEditor, useEditorState, useReactNodeView, useTiptap, useTiptapState };
}
import * as import__tiptap_core from "@tiptap/core";
//#endregion
export { EditorConsumer, EditorContent, EditorContentProps, EditorContext, EditorContextValue, EditorProvider, EditorProviderProps, EditorStateSnapshot, MarkViewContent, MarkViewContentProps, MarkViewContextProps, NodeViewContent, NodeViewContentProps, NodeViewWrapper, NodeViewWrapperProps, PureEditorContent, ReactMarkView, ReactMarkViewContext, ReactMarkViewRenderer, ReactMarkViewRendererOptions, ReactNodeView, ReactNodeViewContentProvider, ReactNodeViewContext, ReactNodeViewContextProps, ReactNodeViewProps, ReactNodeViewRenderer, ReactNodeViewRendererOptions, ReactRenderer, ReactRendererOptions, ReactWidgetDecorationProps, ReactWidgetRenderer, ReactWidgetRendererOptions, Tiptap, TiptapContent, TiptapContext, TiptapContextType, TiptapWrapper, TiptapWrapperEditorInstanceProps, TiptapWrapperProps, UseEditorOptions, UseEditorStateOptions, createContentComponent, useCurrentEditor, useEditor, useEditorState, useReactNodeView, useTiptap, useTiptapState };
//# sourceMappingURL=index.d.ts.map
+1179
-1213
"use client";
// src/Context.tsx
import { createContext, useContext, useMemo } from "react";
// src/EditorContent.tsx
import React, { forwardRef } from "react";
import ReactDOM from "react-dom";
import React, { createContext, createElement, createRef, forwardRef, memo, useContext, useDebugValue, useEffect, useLayoutEffect, useMemo, useRef, useState, version } from "react";
import ReactDOM, { flushSync } 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;
}
});
};
import { Editor, MarkView, NodeView, createWidgetDecoration, getRenderedAttributes, isNodeViewSelected } from "@tiptap/core";
import { deepEqual } from "fast-equals";
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
export * from "@tiptap/core";
//#region src/EditorContent.tsx
const 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) });
/**
* This component renders all of the editor's node views.
*/
const Portals = ({ contentComponent }) => {
const renderers = useSyncExternalStore(contentComponent.subscribe, contentComponent.getSnapshot, contentComponent.getServerSnapshot);
return /* @__PURE__ */ jsx(Fragment, { children: Object.values(renderers) });
};
function createContentComponent() {
const subscribers = /* @__PURE__ */ new Set();
let renderers = {};
let isNotificationQueued = false;
const notifySubscribers = () => {
if (isNotificationQueued || !subscribers.size) {
return;
}
isNotificationQueued = true;
queueMicrotask(() => {
isNotificationQueued = false;
subscribers.forEach((subscriber) => subscriber());
});
};
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)
};
notifySubscribers();
},
/**
* Removes a NodeView Renderer from the editor.
*/
removeRenderer(id) {
const nextRenderers = { ...renderers };
delete nextRenderers[id];
renderers = nextRenderers;
notifySubscribers();
}
};
const subscribers = /* @__PURE__ */ new Set();
let renderers = {};
let isNotificationQueued = false;
const notifySubscribers = () => {
if (isNotificationQueued || !subscribers.size) return;
isNotificationQueued = true;
queueMicrotask(() => {
isNotificationQueued = false;
subscribers.forEach((subscriber) => subscriber());
});
};
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)
};
notifySubscribers();
},
/**
* Removes a NodeView Renderer from the editor.
*/
removeRenderer(id) {
const nextRenderers = { ...renderers };
delete nextRenderers[id];
renderers = nextRenderers;
notifySubscribers();
}
};
}
var PureEditorContent = class extends React.Component {
constructor(props) {
super(props);
this.editorContentRef = React.createRef();
}
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 = createContentComponent();
editor.createNodeViews();
editor.isEditorContentInitialized = true;
this.forceUpdate();
}
}
componentWillUnmount() {
var _a;
const editor = this.props.editor;
if (!editor) {
return;
}
editor.isEditorContentInitialized = false;
if (!editor.isDestroyed) {
editor.view.setProps({
nodeViews: {}
});
}
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 })
] });
}
constructor(props) {
super(props);
this.editorContentRef = React.createRef();
}
componentDidMount() {
this.init();
}
componentDidUpdate() {
this.init();
}
init() {
var _editor$view$dom;
const editor = this.props.editor;
if (editor && !editor.isDestroyed && ((_editor$view$dom = editor.view.dom) === null || _editor$view$dom === void 0 ? void 0 : _editor$view$dom.parentNode)) {
if (editor.contentComponent) return;
const element = this.editorContentRef.current;
element.append(...editor.view.dom.parentNode.childNodes);
editor.setOptions({ element });
editor.contentComponent = createContentComponent();
editor.createNodeViews();
editor.isEditorContentInitialized = true;
this.forceUpdate();
}
}
componentWillUnmount() {
const editor = this.props.editor;
if (!editor) return;
editor.isEditorContentInitialized = false;
if (!editor.isDestroyed) editor.view.setProps({ nodeViews: {} });
editor.contentComponent = null;
try {
var _editor$view$dom2;
if (!((_editor$view$dom2 = editor.view.dom) === null || _editor$view$dom2 === void 0 ? void 0 : _editor$view$dom2.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 || editor === void 0 ? 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;
const 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
});
});
const EditorContent = React.memo(EditorContentWithKey);
//#endregion
//#region src/useEditorState.ts
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : 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.
*/
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) {
let lastTransaction;
const fn = (props) => {
if ((props == null ? void 0 : props.transaction) !== void 0 && props.transaction === lastTransaction) {
return;
}
lastTransaction = props == null ? void 0 : props.transaction;
this.transactionNumber += 1;
this.subscribers.forEach((callback) => callback());
};
const currentEditor = this.editor;
currentEditor.on("transaction", fn);
currentEditor.on("update", fn);
return () => {
currentEditor.off("transaction", fn);
currentEditor.off("update", fn);
};
}
return void 0;
}
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) {
/**
* 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.
*/
let lastTransaction;
const fn = (props) => {
if ((props === null || props === void 0 ? void 0 : props.transaction) !== void 0 && props.transaction === lastTransaction) return;
lastTransaction = props === null || props === void 0 ? void 0 : props.transaction;
this.transactionNumber += 1;
this.subscribers.forEach((callback) => callback());
};
const currentEditor = this.editor;
currentEditor.on("transaction", fn);
currentEditor.on("update", fn);
return () => {
currentEditor.off("transaction", fn);
currentEditor.off("update", fn);
};
}
}
};
/**
* 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] = 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;
var _options$equalityFn;
const [editorStateManager] = useState(() => new EditorStateManager(options.editor));
const selectedState = useSyncExternalStoreWithSelector(editorStateManager.subscribe, editorStateManager.getSnapshot, editorStateManager.getServerSnapshot, options.selector, (_options$equalityFn = options.equalityFn) !== null && _options$equalityFn !== void 0 ? _options$equalityFn : 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() {
const explicit = this.options.current.immediatelyRender;
let immediatelyRender = explicit != null ? explicit : true;
if (isSSR) {
if (immediatelyRender && isDev) {
console.warn(
"SSR detected. `immediatelyRender` has been set to false to avoid hydration mismatches"
);
}
immediatelyRender = false;
} else if (isNext && explicit === void 0) {
immediatelyRender = false;
if (isDev) {
console.warn(
"Next.js detected. `immediatelyRender` defaults to false to avoid hydration mismatches. Pass `immediatelyRender: true` explicitly if you are rendering the editor only on the client."
);
}
}
return immediatelyRender ? this.createEditor() : 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);
},
onMount: (...args) => {
var _a, _b;
return (_b = (_a = this.options.current).onMount) == null ? void 0 : _b.call(_a, ...args);
},
onUnmount: (...args) => {
var _a, _b;
return (_b = (_a = this.options.current).onUnmount) == 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);
}
//#endregion
//#region src/useEditor.ts
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.
*/
var EditorInstanceManager = class EditorInstanceManager {
constructor(options) {
this.editor = null;
this.subscriptions = /* @__PURE__ */ new Set();
this.isComponentMounted = false;
this.previousDeps = null;
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() {
const explicit = this.options.current.immediatelyRender;
let immediatelyRender = explicit !== null && explicit !== void 0 ? explicit : true;
if (isSSR) {
if (immediatelyRender && isDev) console.warn("SSR detected. `immediatelyRender` has been set to false to avoid hydration mismatches");
immediatelyRender = false;
} else if (isNext && explicit === void 0) {
immediatelyRender = false;
if (isDev) console.warn("Next.js detected. `immediatelyRender` defaults to false to avoid hydration mismatches. Pass `immediatelyRender: true` explicitly if you are rendering the editor only on the client.");
}
return immediatelyRender ? this.createEditor() : null;
}
/**
* Create a new editor instance. And attach event listeners.
*/
createEditor() {
return new Editor({
...this.options.current,
onBeforeCreate: (...args) => {
var _this$options$current, _this$options$current2;
return (_this$options$current = (_this$options$current2 = this.options.current).onBeforeCreate) === null || _this$options$current === void 0 ? void 0 : _this$options$current.call(_this$options$current2, ...args);
},
onBlur: (...args) => {
var _this$options$current3, _this$options$current4;
return (_this$options$current3 = (_this$options$current4 = this.options.current).onBlur) === null || _this$options$current3 === void 0 ? void 0 : _this$options$current3.call(_this$options$current4, ...args);
},
onCreate: (...args) => {
var _this$options$current5, _this$options$current6;
return (_this$options$current5 = (_this$options$current6 = this.options.current).onCreate) === null || _this$options$current5 === void 0 ? void 0 : _this$options$current5.call(_this$options$current6, ...args);
},
onDestroy: (...args) => {
var _this$options$current7, _this$options$current8;
return (_this$options$current7 = (_this$options$current8 = this.options.current).onDestroy) === null || _this$options$current7 === void 0 ? void 0 : _this$options$current7.call(_this$options$current8, ...args);
},
onFocus: (...args) => {
var _this$options$current9, _this$options$current10;
return (_this$options$current9 = (_this$options$current10 = this.options.current).onFocus) === null || _this$options$current9 === void 0 ? void 0 : _this$options$current9.call(_this$options$current10, ...args);
},
onSelectionUpdate: (...args) => {
var _this$options$current11, _this$options$current12;
return (_this$options$current11 = (_this$options$current12 = this.options.current).onSelectionUpdate) === null || _this$options$current11 === void 0 ? void 0 : _this$options$current11.call(_this$options$current12, ...args);
},
onTransaction: (...args) => {
var _this$options$current13, _this$options$current14;
return (_this$options$current13 = (_this$options$current14 = this.options.current).onTransaction) === null || _this$options$current13 === void 0 ? void 0 : _this$options$current13.call(_this$options$current14, ...args);
},
onUpdate: (...args) => {
var _this$options$current15, _this$options$current16;
return (_this$options$current15 = (_this$options$current16 = this.options.current).onUpdate) === null || _this$options$current15 === void 0 ? void 0 : _this$options$current15.call(_this$options$current16, ...args);
},
onContentError: (...args) => {
var _this$options$current17, _this$options$current18;
return (_this$options$current17 = (_this$options$current18 = this.options.current).onContentError) === null || _this$options$current17 === void 0 ? void 0 : _this$options$current17.call(_this$options$current18, ...args);
},
onDrop: (...args) => {
var _this$options$current19, _this$options$current20;
return (_this$options$current19 = (_this$options$current20 = this.options.current).onDrop) === null || _this$options$current19 === void 0 ? void 0 : _this$options$current19.call(_this$options$current20, ...args);
},
onPaste: (...args) => {
var _this$options$current21, _this$options$current22;
return (_this$options$current21 = (_this$options$current22 = this.options.current).onPaste) === null || _this$options$current21 === void 0 ? void 0 : _this$options$current21.call(_this$options$current22, ...args);
},
onDelete: (...args) => {
var _this$options$current23, _this$options$current24;
return (_this$options$current23 = (_this$options$current24 = this.options.current).onDelete) === null || _this$options$current23 === void 0 ? void 0 : _this$options$current23.call(_this$options$current24, ...args);
},
onMount: (...args) => {
var _this$options$current25, _this$options$current26;
return (_this$options$current25 = (_this$options$current26 = this.options.current).onMount) === null || _this$options$current25 === void 0 ? void 0 : _this$options$current25.call(_this$options$current26, ...args);
},
onUnmount: (...args) => {
var _this$options$current27, _this$options$current28;
return (_this$options$current27 = (_this$options$current28 = this.options.current).onUnmount) === null || _this$options$current27 === void 0 ? void 0 : _this$options$current27.call(_this$options$current28, ...args);
}
});
}
/**
* 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 _b$extensions;
if (extension !== ((_b$extensions = b.extensions) === null || _b$extensions === void 0 ? void 0 : _b$extensions[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;
}
if (this.previousDeps.length === deps.length && this.previousDeps.every((dep, index) => dep === deps[index])) 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;
const mostRecentOptions = useRef(options);
mostRecentOptions.current = options;
const [instanceManager] = useState(() => new EditorInstanceManager(mostRecentOptions));
const editor = useSyncExternalStore(instanceManager.subscribe, instanceManager.getEditor, instanceManager.getServerSnapshot);
useDebugValue(editor);
useEffect(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
] });
//#endregion
//#region src/Context.tsx
const EditorContext = createContext({ editor: null });
const EditorConsumer = EditorContext.Consumer;
/**
* A hook to get the current editor instance.
*/
const useCurrentEditor = () => 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);
const contextValue = useMemo(() => ({ editor }), [editor]);
if (!editor) return null;
return /* @__PURE__ */ jsxs(EditorContext.Provider, {
value: contextValue,
children: [
slotBefore,
/* @__PURE__ */ jsx(EditorConsumer, { children: ({ editor: currentEditor }) => /* @__PURE__ */ jsx(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: () => {
}
//#endregion
//#region src/useReactNodeView.ts
const ReactNodeViewContext = createContext({
onDragStart: () => {},
nodeViewContentChildren: void 0,
nodeViewContentRef: () => {}
});
var ReactNodeViewContentProvider = ({
children,
content
}) => {
return createElement(
ReactNodeViewContext.Provider,
{ value: { nodeViewContentChildren: content } },
children
);
const 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
}
)
);
const useReactNodeView = () => useContext(ReactNodeViewContext);
//#endregion
//#region src/NodeViewContent.tsx
function NodeViewContent({ as: Tag = "div", ...props }) {
const { nodeViewContentRef, nodeViewContentChildren } = useReactNodeView();
return /* @__PURE__ */ jsx(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
}
}
)
);
//#endregion
//#region src/NodeViewWrapper.tsx
const NodeViewWrapper = React.forwardRef((props, ref) => {
const { onDragStart } = useReactNodeView();
return /* @__PURE__ */ jsx(props.as || "div", {
...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";
//#endregion
//#region src/ReactRenderer.tsx
/**
* Check if a component is a class component.
* @param Component
* @returns {boolean}
*/
function isClassComponent(Component) {
return !!(typeof Component === "function" && Component.prototype && Component.prototype.isReactComponent);
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"));
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"));
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) {
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;
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;
}
/**
* Check if we're running React 19+ by detecting if function components support ref props
* @returns {boolean}
*/
function isReact19Plus() {
try {
if (reactVersion) {
const majorVersion = parseInt(reactVersion.split(".")[0], 10);
return majorVersion >= 19;
}
} catch {
}
return false;
try {
if (version) return parseInt(version.split(".")[0], 10) >= 19;
} catch {}
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',
* })
*/
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.isEditorContentInitialized) {
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.
* Skips the render if none of the supplied props actually changed,
* to avoid unnecessary portal re-creation.
*/
updateProps(props = {}) {
if (this.destroyed) {
return;
}
let changed = false;
const keys = Object.keys(props);
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i];
if (props[key] !== this.props[key]) {
changed = true;
break;
}
}
if (!changed) {
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]);
});
}
/**
* Immediately creates element and renders the provided React component.
*/
constructor(component, { editor, props = {}, as = "div", className = "" }) {
this.ref = null;
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.isEditorContentInitialized) flushSync(() => {
this.render();
});
else queueMicrotask(() => {
if (this.destroyed) return;
this.render();
});
}
/**
* Render the React component.
*/
render() {
var _editor$contentCompon;
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__ */ jsx(Component, { ...elementProps });
editor === null || editor === void 0 || (_editor$contentCompon = editor.contentComponent) === null || _editor$contentCompon === void 0 || _editor$contentCompon.setRenderer(this.id, this);
}
/**
* Re-renders the React component with new props.
* Skips the render if none of the supplied props actually changed,
* to avoid unnecessary portal re-creation.
*/
updateProps(props = {}) {
if (this.destroyed) return;
let changed = false;
const keys = Object.keys(props);
for (let i = 0; i < keys.length; i += 1) {
const key = keys[i];
if (props[key] !== this.props[key]) {
changed = true;
break;
}
}
if (!changed) return;
this.props = {
...this.props,
...props
};
this.render();
}
/**
* Destroy the React component.
*/
destroy() {
var _editor$contentCompon2;
this.destroyed = true;
const editor = this.editor;
editor === null || editor === void 0 || (_editor$contentCompon2 = editor.contentComponent) === null || _editor$contentCompon2 === void 0 || _editor$contentCompon2.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": "" })
);
//#endregion
//#region src/ReactMarkViewRenderer.tsx
const ReactMarkViewContext = React.createContext({ markViewContentRef: () => {} });
const MarkViewContent = (props) => {
const { as: Tag = "span", ...rest } = props;
const { markViewContentRef } = React.useContext(ReactMarkViewContext);
return /* @__PURE__ */ jsx(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;
}
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 = React.memo((componentProps) => {
return /* @__PURE__ */ jsx(ReactMarkViewContext.Provider, {
value: context,
children: React.createElement(component, componentProps)
});
});
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);
return (props) => new ReactMarkView(component, props, options);
}
// src/ReactNodeViewRenderer.tsx
import { getRenderedAttributes, isNodeViewSelected, NodeView } from "@tiptap/core";
import { createElement as createElement2, createRef, memo } from "react";
// src/captureDOMSelection.ts
//#endregion
//#region src/captureDOMSelection.ts
/**
* Saves the DOM selection inside `element`, returns a function to put it back.
* Browsers drop the selection when an element moves, so restore it after the move.
*/
function captureDOMSelection(element) {
var _a, _b;
const root = element.getRootNode();
const selection = typeof root.getSelection === "function" ? root.getSelection() : (_b = (_a = element.ownerDocument) == null ? void 0 : _a.defaultView) == null ? void 0 : _b.getSelection();
if (!selection || selection.rangeCount === 0) {
return null;
}
const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
if (!anchorNode || !focusNode) {
return null;
}
if (!element.contains(anchorNode) || !element.contains(focusNode)) {
return null;
}
return () => {
try {
selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
} catch {
}
};
var _element$ownerDocumen;
const root = element.getRootNode();
const selection = typeof root.getSelection === "function" ? root.getSelection() : (_element$ownerDocumen = element.ownerDocument) === null || _element$ownerDocumen === void 0 || (_element$ownerDocumen = _element$ownerDocumen.defaultView) === null || _element$ownerDocumen === void 0 ? void 0 : _element$ownerDocumen.getSelection();
if (!selection || selection.rangeCount === 0) return null;
const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
if (!anchorNode || !focusNode) return null;
if (!element.contains(anchorNode) || !element.contains(focusNode)) return null;
return () => {
try {
selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
} catch {}
};
}
// src/ReactNodeViewRenderer.tsx
import { jsx as jsx7 } from "react/jsx-runtime";
//#endregion
//#region src/ReactNodeViewRenderer.tsx
var ReactNodeView = class extends NodeView {
constructor(component, props, options) {
super(component, props, options);
/**
* The requestAnimationFrame ID used for selection updates.
*/
this.selectionRafId = null;
/**
* Fires on editor updates when trackNodeViewPosition is enabled.
* Detects position shifts where update() is NOT called (e.g. typing above).
*/
this.handlePositionUpdate = () => {
const newPos = this.getPos();
if (typeof newPos !== "number" || newPos === this.currentPos) {
return;
}
this.currentPos = newPos;
this.renderer.updateProps({ getPos: () => this.getPos() });
if (typeof this.options.attrs === "function") {
this.updateElementAttributes();
}
};
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) {
contentTarget.appendChild(this.contentDOMElement);
} else {
this.dom.appendChild(this.contentDOMElement);
}
}
if (this.options.trackNodeViewPosition) {
this.editor.on("update", this.handlePositionUpdate);
}
}
/**
* 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()
};
const mountProps = props;
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");
}
const restoreSelection = captureDOMSelection(this.contentDOMElement);
element.appendChild(this.contentDOMElement);
restoreSelection == null ? void 0 : restoreSelection();
}
};
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: mountProps,
as,
className: `node-${this.node.type.name} ${className}`.trim()
});
this.editor.on("selectionUpdate", this.handleSelectionUpdate);
this.updateElementAttributes();
this.currentPos = this.getPos();
}
/**
* 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(() => {
var _a;
this.selectionRafId = null;
const pos = (_a = this.getPos()) != null ? _a : this.currentPos;
if (typeof pos !== "number") {
return;
}
const isSelected = isNodeViewSelected({
selection: this.editor.state.selection,
pos,
nodeSize: this.node.nodeSize,
selectedOnTextSelection: this.options.selectedOnTextSelection
});
if (isSelected) {
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;
this.currentPos = this.getPos();
return this.options.update({
oldNode,
oldDecorations,
newNode: node,
newDecorations: decorations,
oldInnerDecorations,
innerDecorations,
updateProps: () => rerenderComponent({
node,
decorations,
innerDecorations,
extension: this.extensionWithSyncedStorage
})
});
}
const nodeChanged = node !== this.node;
if (!nodeChanged) {
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
return true;
}
const newPos = this.getPos();
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
this.currentPos = newPos;
const extraProps = {
node,
decorations,
innerDecorations,
extension: this.extensionWithSyncedStorage
};
if (this.options.trackNodeViewPosition) {
extraProps.getPos = () => this.getPos();
}
rerenderComponent(extraProps);
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);
if (this.options.trackNodeViewPosition) {
this.editor.off("update", this.handlePositionUpdate);
}
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);
}
}
constructor(component, props, options) {
super(component, props, options);
this.selectionRafId = null;
this.handlePositionUpdate = () => {
const newPos = this.getPos();
if (typeof newPos !== "number" || newPos === this.currentPos) return;
this.currentPos = newPos;
this.renderer.updateProps({ getPos: () => this.getPos() });
if (typeof this.options.attrs === "function") this.updateElementAttributes();
};
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) contentTarget.appendChild(this.contentDOMElement);
else this.dom.appendChild(this.contentDOMElement);
}
if (this.options.trackNodeViewPosition) this.editor.on("update", this.handlePositionUpdate);
}
/**
* 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) {
if (prop === "storage") {
var _editor$storage;
return (_editor$storage = editor.storage[extension.name]) !== null && _editor$storage !== void 0 ? _editor$storage : {};
}
return Reflect.get(target, prop, receiver);
} });
}
return this.cachedExtensionWithSyncedStorage;
}
/**
* Setup the React component.
* Called on initialization.
*/
mount() {
const mountProps = {
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");
const restoreSelection = captureDOMSelection(this.contentDOMElement);
element.appendChild(this.contentDOMElement);
restoreSelection === null || restoreSelection === void 0 || restoreSelection();
}
};
const context = {
onDragStart,
nodeViewContentRef
};
const Component = this.component;
const ReactNodeViewProvider = memo((componentProps) => {
return /* @__PURE__ */ jsx(ReactNodeViewContext.Provider, {
value: context,
children: 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: mountProps,
as,
className: `node-${this.node.type.name} ${className}`.trim()
});
this.editor.on("selectionUpdate", this.handleSelectionUpdate);
this.updateElementAttributes();
this.currentPos = this.getPos();
}
/**
* Return the DOM element.
* This is the element that will be used to display the node view.
*/
get dom() {
var _this$renderer$elemen;
if (this.renderer.element.firstElementChild && !((_this$renderer$elemen = this.renderer.element.firstElementChild) === null || _this$renderer$elemen === void 0 ? void 0 : _this$renderer$elemen.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(() => {
var _this$getPos;
this.selectionRafId = null;
const pos = (_this$getPos = this.getPos()) !== null && _this$getPos !== void 0 ? _this$getPos : this.currentPos;
if (typeof pos !== "number") return;
if (isNodeViewSelected({
selection: this.editor.state.selection,
pos,
nodeSize: this.node.nodeSize,
selectedOnTextSelection: this.options.selectedOnTextSelection
})) {
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;
this.currentPos = this.getPos();
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.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
return true;
}
const newPos = this.getPos();
this.node = node;
this.decorations = decorations;
this.innerDecorations = innerDecorations;
this.currentPos = newPos;
const extraProps = {
node,
decorations,
innerDecorations,
extension: this.extensionWithSyncedStorage
};
if (this.options.trackNodeViewPosition) extraProps.getPos = () => this.getPos();
rerenderComponent(extraProps);
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);
if (this.options.trackNodeViewPosition) this.editor.off("update", this.handlePositionUpdate);
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);
}
}
};
/**
* Create a React node view renderer.
*/
function ReactNodeViewRenderer(component, options) {
return (props) => {
if (!props.editor.contentComponent) {
return {};
}
return new ReactNodeView(component, props, options);
};
return (props) => {
if (!props.editor.contentComponent) return {
dom: document.createElement("span"),
contentDOM: null,
update: () => false,
destroy: () => {},
selectNode: () => {},
deselectNode: () => {},
stopEvent: () => false,
ignoreMutation: () => true
};
return new ReactNodeView(component, props, options);
};
}
// src/ReactWidgetRenderer.tsx
import { createWidgetDecoration } from "@tiptap/core";
var WIDGET_CACHE = /* @__PURE__ */ Symbol("tiptapReactWidgetCache");
//#endregion
//#region src/ReactWidgetRenderer.tsx
const WIDGET_CACHE = Symbol("tiptapReactWidgetCache");
/**
* Renders a React component into a ProseMirror widget decoration.
* Reuses Tiptap's `ReactRenderer` so the component lives in the editor's
* React tree (context and hooks work as usual). Use a stable `key` for
* stateful widgets.
* @example
* addDecorations() {
* return {
* create: ({ editor, state }) =>
* findMatches(state.doc).map(match =>
* ReactWidgetRenderer(MyWidget, {
* editor, pos: match.pos, key: `match-${match.id}`,
* props: { label: match.label },
* }),
* ),
* }
* }
*/
function ReactWidgetRenderer(component, options) {
const { editor, props = {}, as = "span", className } = options;
return createWidgetDecoration({
// Forwards editor, pos, key and the ProseMirror widget options unchanged.
...options,
props,
cacheKey: WIDGET_CACHE,
context: (getPos) => ({ editor, getPos }),
create: (renderProps) => new ReactRenderer(component, {
editor,
as,
className,
props: renderProps
}),
materialize: (renderer) => {
renderer.render();
return renderer.element;
}
});
const { editor, props = {}, as = "span", className } = options;
return createWidgetDecoration({
...options,
props,
cacheKey: WIDGET_CACHE,
context: (getPos) => ({
editor,
getPos
}),
create: (renderProps) => new ReactRenderer(component, {
editor,
as,
className,
props: renderProps
}),
materialize: (renderer) => {
renderer.render();
return renderer.element;
}
});
}
// 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 <Tiptap> provider");
}
});
//#endregion
//#region src/Tiptap.tsx
/**
* React context that stores the current editor instance.
*
* Use `useTiptap()` to read from this context in child components.
*/
const TiptapContext = createContext({ get editor() {
throw new Error("useTiptap must be used within a <Tiptap> provider");
} });
TiptapContext.displayName = "TiptapContext";
var useTiptap = () => useContext3(TiptapContext);
/**
* Hook to read the Tiptap context and access the editor instance.
*
* This is a small convenience wrapper around `useContext(TiptapContext)`.
* The editor is always available when used within a `<Tiptap>` provider.
*
* @returns The current `TiptapContextType` value from the provider.
*
* @example
* ```tsx
* import { useTiptap } from '@tiptap/react'
*
* function Toolbar() {
* const { editor } = useTiptap()
*
* return (
* <button onClick={() => editor.chain().focus().toggleBold().run()}>
* Bold
* </button>
* )
* }
* ```
*/
const useTiptap = () => useContext(TiptapContext);
/**
* Select a slice of the editor state using the context-provided editor.
*
* This is a thin wrapper around `useEditorState` that reads the `editor`
* instance from `useTiptap()` so callers don't have to pass it manually.
*
* @typeParam TSelectorResult - The type returned by the selector.
* @param selector - Function that receives the editor state snapshot and
* returns the piece of state you want to subscribe to.
* @param equalityFn - Optional function to compare previous/next selected
* values and avoid unnecessary updates.
* @returns The selected slice of the editor state.
*
* @example
* ```tsx
* function WordCount() {
* const wordCount = useTiptapState(state => {
* const text = state.editor.state.doc.textContent
* return text.split(/\s+/).filter(Boolean).length
* })
*
* return <span>{wordCount} words</span>
* }
* ```
*/
function useTiptapState(selector, equalityFn) {
const { editor } = useTiptap();
return useEditorState({
editor,
selector,
equalityFn
});
const { editor } = useTiptap();
return useEditorState({
editor,
selector,
equalityFn
});
}
/**
* Top-level provider component that makes the editor instance available via
* React context to all child components.
*
* This component also provides backwards compatibility with the legacy
* `EditorContext`, so components using `useCurrentEditor()` will work
* inside a `<Tiptap>` provider.
*
* @param props - Component props.
* @returns A context provider element wrapping `children`.
*
* @example
* ```tsx
* import { Tiptap, useEditor } from '@tiptap/react'
*
* function App() {
* const editor = useEditor({ extensions: [...] })
*
* return (
* <Tiptap editor={editor}>
* <Toolbar />
* <Tiptap.Content />
* </Tiptap>
* )
* }
* ```
*/
function TiptapWrapper({ children, ...props }) {
const resolvedEditor = "editor" in props ? props.editor : props.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 }) });
const resolvedEditor = "editor" in props ? props.editor : props.instance;
if (!resolvedEditor) throw new Error("Tiptap: An editor instance is required. Pass a non-null `editor` prop.");
const tiptapContextValue = useMemo(() => ({ editor: resolvedEditor }), [resolvedEditor]);
const legacyContextValue = useMemo(() => ({ editor: resolvedEditor }), [resolvedEditor]);
return /* @__PURE__ */ jsx(EditorContext.Provider, {
value: legacyContextValue,
children: /* @__PURE__ */ jsx(TiptapContext.Provider, {
value: tiptapContextValue,
children
})
});
}
TiptapWrapper.displayName = "Tiptap";
/**
* Convenience component that renders `EditorContent` using the context-provided
* editor instance. Use this instead of manually passing the `editor` prop.
*
* @param props - All `EditorContent` props except `editor` and `ref`.
* @returns An `EditorContent` element bound to the context editor.
*
* @example
* ```tsx
* // inside a Tiptap provider
* <Tiptap.Content className="editor" />
* ```
*/
function TiptapContent({ ...rest }) {
const { editor } = useTiptap();
return /* @__PURE__ */ jsx8(EditorContent, { editor, ...rest });
const { editor } = useTiptap();
return /* @__PURE__ */ jsx(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
});
/**
* Root `Tiptap` component. Use it as the provider for all child components.
*
* The exported object includes the `Content` subcomponent for rendering the
* editor content area.
*
* This component provides both the new `TiptapContext` (accessed via `useTiptap()`)
* and the legacy `EditorContext` (accessed via `useCurrentEditor()`) for
* backwards compatibility.
*
* For bubble menus and floating menus, import them separately from
* `@tiptap/react/menus` to keep floating-ui as an optional dependency.
*
* @example
* ```tsx
* import { Tiptap, useEditor } from '@tiptap/react'
* import { BubbleMenu } from '@tiptap/react/menus'
*
* function App() {
* const editor = useEditor({ extensions: [...] })
*
* return (
* <Tiptap editor={editor}>
* <Tiptap.Content />
* <BubbleMenu>
* <button onClick={() => editor.chain().focus().toggleBold().run()}>Bold</button>
* </BubbleMenu>
* </Tiptap>
* )
* }
* ```
*/
const Tiptap = Object.assign(TiptapWrapper, {
/**
* The Tiptap Content component that renders the EditorContent with the editor instance from the context.
* @see TiptapContent
*/
Content: TiptapContent });
//#endregion
export { EditorConsumer, EditorContent, EditorContext, EditorProvider, MarkViewContent, NodeViewContent, NodeViewWrapper, PureEditorContent, ReactMarkView, ReactMarkViewContext, ReactMarkViewRenderer, ReactNodeView, ReactNodeViewContentProvider, ReactNodeViewContext, ReactNodeViewRenderer, ReactRenderer, ReactWidgetRenderer, Tiptap, TiptapContent, TiptapContext, TiptapWrapper, createContentComponent, useCurrentEditor, useEditor, useEditorState, useReactNodeView, useTiptap, useTiptapState };
// src/index.ts
export * from "@tiptap/core";
export {
EditorConsumer,
EditorContent,
EditorContext,
EditorProvider,
MarkViewContent,
NodeViewContent,
NodeViewWrapper,
PureEditorContent,
ReactMarkView,
ReactMarkViewContext,
ReactMarkViewRenderer,
ReactNodeView,
ReactNodeViewContentProvider,
ReactNodeViewContext,
ReactNodeViewRenderer,
ReactRenderer,
ReactWidgetRenderer,
Tiptap,
TiptapContent,
TiptapContext,
TiptapWrapper,
createContentComponent,
useCurrentEditor,
useEditor,
useEditorState,
useReactNodeView,
useTiptap,
useTiptapState
};
//# sourceMappingURL=index.js.map

@@ -1,3 +0,4 @@

"use strict";
"use client";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
//#region \0rolldown/runtime.js
var __create = Object.create;

@@ -9,525 +10,461 @@ var __defProp = Object.defineProperty;

var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/menus/index.ts
var index_exports = {};
__export(index_exports, {
BubbleMenu: () => BubbleMenu,
FloatingMenu: () => FloatingMenu
});
module.exports = __toCommonJS(index_exports);
// src/menus/BubbleMenu.tsx
var import_extension_bubble_menu = require("@tiptap/extension-bubble-menu");
var import_react2 = require("@tiptap/react");
var import_react3 = __toESM(require("react"), 1);
var import_react_dom = require("react-dom");
// src/menus/getAutoPluginKey.ts
var import_state = require("@tiptap/pm/state");
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let _tiptap_extension_bubble_menu = require("@tiptap/extension-bubble-menu");
let _tiptap_react = require("@tiptap/react");
let react = require("react");
react = __toESM(react, 1);
let react_dom = require("react-dom");
let _tiptap_pm_state = require("@tiptap/pm/state");
let _tiptap_extension_floating_menu = require("@tiptap/extension-floating-menu");
//#region src/menus/getAutoPluginKey.ts
function getAutoPluginKey(pluginKey, defaultName) {
return pluginKey != null ? pluginKey : new import_state.PluginKey(defaultName);
return pluginKey !== null && pluginKey !== void 0 ? pluginKey : new _tiptap_pm_state.PluginKey(defaultName);
}
// src/menus/useMenuElementProps.ts
var import_react = require("react");
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? import_react.useLayoutEffect : import_react.useEffect;
var PLUGIN_MANAGED_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
"left",
"opacity",
"position",
"top",
"visibility",
"width"
//#endregion
//#region src/menus/useMenuElementProps.ts
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? react.useLayoutEffect : react.useEffect;
const PLUGIN_MANAGED_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
"left",
"opacity",
"position",
"top",
"visibility",
"width"
]);
var UNITLESS_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
"animationIterationCount",
"aspectRatio",
"borderImageOutset",
"borderImageSlice",
"borderImageWidth",
"columnCount",
"columns",
"fillOpacity",
"flex",
"flexGrow",
"flexShrink",
"fontWeight",
"gridArea",
"gridColumn",
"gridColumnEnd",
"gridColumnStart",
"gridRow",
"gridRowEnd",
"gridRowStart",
"lineClamp",
"lineHeight",
"opacity",
"order",
"orphans",
"scale",
"stopOpacity",
"strokeDasharray",
"strokeDashoffset",
"strokeMiterlimit",
"strokeOpacity",
"strokeWidth",
"tabSize",
"widows",
"zIndex",
"zoom"
const UNITLESS_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
"animationIterationCount",
"aspectRatio",
"borderImageOutset",
"borderImageSlice",
"borderImageWidth",
"columnCount",
"columns",
"fillOpacity",
"flex",
"flexGrow",
"flexShrink",
"fontWeight",
"gridArea",
"gridColumn",
"gridColumnEnd",
"gridColumnStart",
"gridRow",
"gridRowEnd",
"gridRowStart",
"lineClamp",
"lineHeight",
"opacity",
"order",
"orphans",
"scale",
"stopOpacity",
"strokeDasharray",
"strokeDashoffset",
"strokeMiterlimit",
"strokeOpacity",
"strokeWidth",
"tabSize",
"widows",
"zIndex",
"zoom"
]);
var ATTRIBUTE_EXCLUSIONS = /* @__PURE__ */ new Set(["children", "className", "style"]);
var DIRECT_PROPERTY_KEYS = /* @__PURE__ */ new Set(["tabIndex"]);
var FORWARDED_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
"accessKey",
"autoCapitalize",
"contentEditable",
"contextMenu",
"dir",
"draggable",
"enterKeyHint",
"hidden",
"id",
"lang",
"nonce",
"role",
"slot",
"spellCheck",
"tabIndex",
"title",
"translate"
const ATTRIBUTE_EXCLUSIONS = /* @__PURE__ */ new Set([
"children",
"className",
"style"
]);
var SPECIAL_EVENT_NAMES = {
Blur: "focusout",
DoubleClick: "dblclick",
Focus: "focusin",
MouseEnter: "mouseenter",
MouseLeave: "mouseleave"
const DIRECT_PROPERTY_KEYS = /* @__PURE__ */ new Set(["tabIndex"]);
const FORWARDED_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
"accessKey",
"autoCapitalize",
"contentEditable",
"contextMenu",
"dir",
"draggable",
"enterKeyHint",
"hidden",
"id",
"lang",
"nonce",
"role",
"slot",
"spellCheck",
"tabIndex",
"title",
"translate"
]);
const SPECIAL_EVENT_NAMES = {
Blur: "focusout",
DoubleClick: "dblclick",
Focus: "focusin",
MouseEnter: "mouseenter",
MouseLeave: "mouseleave"
};
function isEventProp(key, value) {
return /^on[A-Z]/.test(key) && typeof value === "function";
return /^on[A-Z]/.test(key) && typeof value === "function";
}
function toAttributeName(key) {
if (key.startsWith("aria-") || key.startsWith("data-")) {
return key;
}
return key;
if (key.startsWith("aria-") || key.startsWith("data-")) return key;
return key;
}
function isForwardedAttributeKey(key) {
return key.startsWith("aria-") || key.startsWith("data-") || FORWARDED_ATTRIBUTE_KEYS.has(key);
return key.startsWith("aria-") || key.startsWith("data-") || FORWARDED_ATTRIBUTE_KEYS.has(key);
}
function toStylePropertyName(key) {
if (key.startsWith("--")) {
return key;
}
return key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
if (key.startsWith("--")) return key;
return key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
}
function toEventConfig(key) {
var _a;
const useCapture = key.endsWith("Capture");
const baseKey = useCapture ? key.slice(0, -7) : key;
const reactEventName = baseKey.slice(2);
const eventName = (_a = SPECIAL_EVENT_NAMES[reactEventName]) != null ? _a : reactEventName.toLowerCase();
return {
eventName,
options: useCapture ? { capture: true } : void 0
};
var _SPECIAL_EVENT_NAMES$;
const useCapture = key.endsWith("Capture");
const reactEventName = (useCapture ? key.slice(0, -7) : key).slice(2);
return {
eventName: (_SPECIAL_EVENT_NAMES$ = SPECIAL_EVENT_NAMES[reactEventName]) !== null && _SPECIAL_EVENT_NAMES$ !== void 0 ? _SPECIAL_EVENT_NAMES$ : reactEventName.toLowerCase(),
options: useCapture ? { capture: true } : void 0
};
}
function createSyntheticEvent(element, nativeEvent) {
let defaultPrevented = nativeEvent.defaultPrevented;
let propagationStopped = false;
const syntheticEvent = Object.create(nativeEvent);
Object.defineProperties(syntheticEvent, {
nativeEvent: { value: nativeEvent },
currentTarget: { value: element },
target: { value: nativeEvent.target },
persist: { value: () => void 0 },
isDefaultPrevented: { value: () => defaultPrevented },
isPropagationStopped: { value: () => propagationStopped },
preventDefault: {
value: () => {
defaultPrevented = true;
nativeEvent.preventDefault();
}
},
stopPropagation: {
value: () => {
propagationStopped = true;
nativeEvent.stopPropagation();
}
}
});
return syntheticEvent;
let defaultPrevented = nativeEvent.defaultPrevented;
let propagationStopped = false;
const syntheticEvent = Object.create(nativeEvent);
Object.defineProperties(syntheticEvent, {
nativeEvent: { value: nativeEvent },
currentTarget: { value: element },
target: { value: nativeEvent.target },
persist: { value: () => void 0 },
isDefaultPrevented: { value: () => defaultPrevented },
isPropagationStopped: { value: () => propagationStopped },
preventDefault: { value: () => {
defaultPrevented = true;
nativeEvent.preventDefault();
} },
stopPropagation: { value: () => {
propagationStopped = true;
nativeEvent.stopPropagation();
} }
});
return syntheticEvent;
}
function isDirectPropertyKey(key) {
return DIRECT_PROPERTY_KEYS.has(key);
return DIRECT_PROPERTY_KEYS.has(key);
}
function setDirectProperty(element, key, value) {
if (key === "tabIndex") {
element.tabIndex = Number(value);
return;
}
;
element[key] = value;
if (key === "tabIndex") {
element.tabIndex = Number(value);
return;
}
element[key] = value;
}
function clearDirectProperty(element, key) {
if (key === "tabIndex") {
element.removeAttribute("tabindex");
return;
}
const propertyValue = element[key];
if (typeof propertyValue === "boolean") {
;
element[key] = false;
return;
}
if (typeof propertyValue === "number") {
;
element[key] = 0;
return;
}
;
element[key] = "";
if (key === "tabIndex") {
element.removeAttribute("tabindex");
return;
}
const propertyValue = element[key];
if (typeof propertyValue === "boolean") {
element[key] = false;
return;
}
if (typeof propertyValue === "number") {
element[key] = 0;
return;
}
element[key] = "";
}
function toStyleValue(styleName, value) {
if (typeof value !== "number" || value === 0 || styleName.startsWith("--") || UNITLESS_STYLE_PROPERTIES.has(styleName)) {
return String(value);
}
return `${value}px`;
if (typeof value !== "number" || value === 0 || styleName.startsWith("--") || UNITLESS_STYLE_PROPERTIES.has(styleName)) return String(value);
return `${value}px`;
}
function removeStyleProperty(element, styleName) {
if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {
return;
}
element.style.removeProperty(toStylePropertyName(styleName));
if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) return;
element.style.removeProperty(toStylePropertyName(styleName));
}
function applyStyleProperty(element, styleName, value) {
if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {
return;
}
element.style.setProperty(toStylePropertyName(styleName), toStyleValue(styleName, value));
if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) return;
element.style.setProperty(toStylePropertyName(styleName), toStyleValue(styleName, value));
}
function syncAttributes(element, prevProps, nextProps) {
const allKeys = /* @__PURE__ */ new Set([...Object.keys(prevProps), ...Object.keys(nextProps)]);
allKeys.forEach((key) => {
if (ATTRIBUTE_EXCLUSIONS.has(key) || !isForwardedAttributeKey(key) || isEventProp(key, prevProps[key]) || isEventProp(key, nextProps[key])) {
return;
}
const prevValue = prevProps[key];
const nextValue = nextProps[key];
if (prevValue === nextValue) {
return;
}
const attributeName = toAttributeName(key);
if (nextValue == null || nextValue === false) {
if (isDirectPropertyKey(key)) {
clearDirectProperty(element, key);
}
element.removeAttribute(attributeName);
return;
}
if (nextValue === true) {
if (isDirectPropertyKey(key)) {
setDirectProperty(element, key, true);
}
element.setAttribute(attributeName, "");
return;
}
if (isDirectPropertyKey(key)) {
setDirectProperty(element, key, nextValue);
return;
}
element.setAttribute(attributeName, String(nextValue));
});
(/* @__PURE__ */ new Set([...Object.keys(prevProps), ...Object.keys(nextProps)])).forEach((key) => {
if (ATTRIBUTE_EXCLUSIONS.has(key) || !isForwardedAttributeKey(key) || isEventProp(key, prevProps[key]) || isEventProp(key, nextProps[key])) return;
const prevValue = prevProps[key];
const nextValue = nextProps[key];
if (prevValue === nextValue) return;
const attributeName = toAttributeName(key);
if (nextValue == null || nextValue === false) {
if (isDirectPropertyKey(key)) clearDirectProperty(element, key);
element.removeAttribute(attributeName);
return;
}
if (nextValue === true) {
if (isDirectPropertyKey(key)) setDirectProperty(element, key, true);
element.setAttribute(attributeName, "");
return;
}
if (isDirectPropertyKey(key)) {
setDirectProperty(element, key, nextValue);
return;
}
element.setAttribute(attributeName, String(nextValue));
});
}
function syncClassName(element, prevClassName, nextClassName) {
if (prevClassName === nextClassName) {
return;
}
if (nextClassName) {
element.className = nextClassName;
return;
}
element.removeAttribute("class");
if (prevClassName === nextClassName) return;
if (nextClassName) {
element.className = nextClassName;
return;
}
element.removeAttribute("class");
}
function syncStyles(element, prevStyle, nextStyle) {
const previousStyle = prevStyle != null ? prevStyle : {};
const currentStyle = nextStyle != null ? nextStyle : {};
const allStyleNames = /* @__PURE__ */ new Set([...Object.keys(previousStyle), ...Object.keys(currentStyle)]);
allStyleNames.forEach((styleName) => {
const prevValue = previousStyle[styleName];
const nextValue = currentStyle[styleName];
if (prevValue === nextValue) {
return;
}
if (nextValue == null) {
removeStyleProperty(element, styleName);
return;
}
applyStyleProperty(element, styleName, nextValue);
});
const previousStyle = prevStyle !== null && prevStyle !== void 0 ? prevStyle : {};
const currentStyle = nextStyle !== null && nextStyle !== void 0 ? nextStyle : {};
(/* @__PURE__ */ new Set([...Object.keys(previousStyle), ...Object.keys(currentStyle)])).forEach((styleName) => {
const prevValue = previousStyle[styleName];
const nextValue = currentStyle[styleName];
if (prevValue === nextValue) return;
if (nextValue == null) {
removeStyleProperty(element, styleName);
return;
}
applyStyleProperty(element, styleName, nextValue);
});
}
function syncEventListeners(element, prevListeners, nextProps) {
prevListeners.forEach(({ eventName, listener, options }) => {
element.removeEventListener(eventName, listener, options);
});
const nextListeners = [];
Object.entries(nextProps).forEach(([key, value]) => {
if (!isEventProp(key, value)) {
return;
}
const { eventName, options } = toEventConfig(key);
const listener = (event) => {
value(createSyntheticEvent(element, event));
};
element.addEventListener(eventName, listener, options);
nextListeners.push({ eventName, listener, options });
});
return nextListeners;
prevListeners.forEach(({ eventName, listener, options }) => {
element.removeEventListener(eventName, listener, options);
});
const nextListeners = [];
Object.entries(nextProps).forEach(([key, value]) => {
if (!isEventProp(key, value)) return;
const { eventName, options } = toEventConfig(key);
const listener = (event) => {
value(createSyntheticEvent(element, event));
};
element.addEventListener(eventName, listener, options);
nextListeners.push({
eventName,
listener,
options
});
});
return nextListeners;
}
function useMenuElementProps(element, props) {
const previousPropsRef = (0, import_react.useRef)({});
const listenersRef = (0, import_react.useRef)([]);
useIsomorphicLayoutEffect(() => {
const previousProps = previousPropsRef.current;
syncClassName(element, previousProps.className, props.className);
syncStyles(element, previousProps.style, props.style);
syncAttributes(element, previousProps, props);
listenersRef.current = syncEventListeners(element, listenersRef.current, props);
previousPropsRef.current = props;
return () => {
listenersRef.current.forEach(({ eventName, listener, options }) => {
element.removeEventListener(eventName, listener, options);
});
listenersRef.current = [];
};
}, [element, props]);
const previousPropsRef = (0, react.useRef)({});
const listenersRef = (0, react.useRef)([]);
useIsomorphicLayoutEffect(() => {
const previousProps = previousPropsRef.current;
syncClassName(element, previousProps.className, props.className);
syncStyles(element, previousProps.style, props.style);
syncAttributes(element, previousProps, props);
listenersRef.current = syncEventListeners(element, listenersRef.current, props);
previousPropsRef.current = props;
return () => {
listenersRef.current.forEach(({ eventName, listener, options }) => {
element.removeEventListener(eventName, listener, options);
});
listenersRef.current = [];
};
}, [element, props]);
}
//#endregion
//#region src/menus/BubbleMenu.tsx
const BubbleMenu = react.default.forwardRef(({ pluginKey, editor, updateDelay, resizeDelay, appendTo, shouldShow = null, getReferencedVirtualElement, options, children, ...restProps }, ref) => {
const menuEl = (0, react.useRef)(document.createElement("div"));
const resolvedPluginKey = (0, react.useRef)(getAutoPluginKey(pluginKey, "bubbleMenu")).current;
useMenuElementProps(menuEl.current, restProps);
if (typeof ref === "function") ref(menuEl.current);
else if (ref) ref.current = menuEl.current;
const { editor: currentEditor } = (0, _tiptap_react.useCurrentEditor)();
/**
* The editor instance where the bubble menu plugin will be registered.
*/
const pluginEditor = editor || currentEditor;
const bubbleMenuPluginProps = {
updateDelay,
resizeDelay,
appendTo,
pluginKey: resolvedPluginKey,
shouldShow,
getReferencedVirtualElement,
options
};
/**
* The props for the bubble menu plugin. They are accessed inside a ref to
* avoid running the useEffect hook and re-registering the plugin when the
* props change.
*/
const bubbleMenuPluginPropsRef = (0, react.useRef)(bubbleMenuPluginProps);
bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps;
/**
* Track whether the plugin has been initialized, so we only send updates
* after the initial registration.
*/
const [pluginInitialized, setPluginInitialized] = (0, react.useState)(false);
/**
* Track whether we need to skip the first options update dispatch.
* This prevents unnecessary updates right after plugin initialization.
*/
const skipFirstUpdateRef = (0, react.useRef)(true);
(0, react.useEffect)(() => {
if (pluginEditor === null || pluginEditor === void 0 ? void 0 : pluginEditor.isDestroyed) return;
if (!pluginEditor) {
console.warn("BubbleMenu component is not rendered inside of an editor component or does not have editor prop.");
return;
}
const bubbleMenuElement = menuEl.current;
bubbleMenuElement.style.visibility = "hidden";
bubbleMenuElement.style.position = "absolute";
const plugin = (0, _tiptap_extension_bubble_menu.BubbleMenuPlugin)({
...bubbleMenuPluginPropsRef.current,
editor: pluginEditor,
element: bubbleMenuElement
});
pluginEditor.registerPlugin(plugin);
const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey;
skipFirstUpdateRef.current = true;
setPluginInitialized(true);
return () => {
setPluginInitialized(false);
pluginEditor.unregisterPlugin(createdPluginKey);
window.requestAnimationFrame(() => {
if (bubbleMenuElement.parentNode) bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
});
};
}, [pluginEditor]);
/**
* Update the plugin options when props change after the plugin has been initialized.
* This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.
*/
(0, react.useEffect)(() => {
if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) return;
if (skipFirstUpdateRef.current) {
skipFirstUpdateRef.current = false;
return;
}
pluginEditor.view.dispatch(pluginEditor.state.tr.setMeta(resolvedPluginKey, {
type: "updateOptions",
options: bubbleMenuPluginPropsRef.current
}));
}, [
pluginInitialized,
pluginEditor,
updateDelay,
resizeDelay,
shouldShow,
options,
appendTo,
getReferencedVirtualElement,
resolvedPluginKey
]);
return (0, react_dom.createPortal)(children, menuEl.current);
});
//#endregion
//#region src/menus/FloatingMenu.tsx
const FloatingMenu = react.default.forwardRef(({ pluginKey, editor, updateDelay, resizeDelay, appendTo, shouldShow = null, options, children, ...restProps }, ref) => {
const menuEl = (0, react.useRef)(document.createElement("div"));
const resolvedPluginKey = (0, react.useRef)(getAutoPluginKey(pluginKey, "floatingMenu")).current;
useMenuElementProps(menuEl.current, restProps);
if (typeof ref === "function") ref(menuEl.current);
else if (ref) ref.current = menuEl.current;
const { editor: currentEditor } = (0, _tiptap_react.useCurrentEditor)();
/**
* The editor instance where the floating menu plugin will be registered.
*/
const pluginEditor = editor || currentEditor;
const floatingMenuPluginProps = {
updateDelay,
resizeDelay,
appendTo,
pluginKey: resolvedPluginKey,
shouldShow,
options
};
/**
* The props for the floating menu plugin. They are accessed inside a ref to
* avoid running the useEffect hook and re-registering the plugin when the
* props change.
*/
const floatingMenuPluginPropsRef = (0, react.useRef)(floatingMenuPluginProps);
floatingMenuPluginPropsRef.current = floatingMenuPluginProps;
/**
* Track whether the plugin has been initialized, so we only send updates
* after the initial registration.
*/
const [pluginInitialized, setPluginInitialized] = (0, react.useState)(false);
/**
* Track whether we need to skip the first options update dispatch.
* This prevents unnecessary updates right after plugin initialization.
*/
const skipFirstUpdateRef = (0, react.useRef)(true);
(0, react.useEffect)(() => {
if (pluginEditor === null || pluginEditor === void 0 ? void 0 : pluginEditor.isDestroyed) return;
if (!pluginEditor) {
console.warn("FloatingMenu component is not rendered inside of an editor component or does not have editor prop.");
return;
}
const floatingMenuElement = menuEl.current;
floatingMenuElement.style.visibility = "hidden";
floatingMenuElement.style.position = "absolute";
const plugin = (0, _tiptap_extension_floating_menu.FloatingMenuPlugin)({
...floatingMenuPluginPropsRef.current,
editor: pluginEditor,
element: floatingMenuElement
});
pluginEditor.registerPlugin(plugin);
const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey;
skipFirstUpdateRef.current = true;
setPluginInitialized(true);
return () => {
setPluginInitialized(false);
pluginEditor.unregisterPlugin(createdPluginKey);
window.requestAnimationFrame(() => {
if (floatingMenuElement.parentNode) floatingMenuElement.parentNode.removeChild(floatingMenuElement);
});
};
}, [pluginEditor]);
/**
* Update the plugin options when props change after the plugin has been initialized.
* This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.
*/
(0, react.useEffect)(() => {
if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) return;
if (skipFirstUpdateRef.current) {
skipFirstUpdateRef.current = false;
return;
}
pluginEditor.view.dispatch(pluginEditor.state.tr.setMeta(resolvedPluginKey, {
type: "updateOptions",
options: floatingMenuPluginPropsRef.current
}));
}, [
pluginInitialized,
pluginEditor,
updateDelay,
resizeDelay,
shouldShow,
options,
appendTo,
resolvedPluginKey
]);
return (0, react_dom.createPortal)(children, menuEl.current);
});
//#endregion
exports.BubbleMenu = BubbleMenu;
exports.FloatingMenu = FloatingMenu;
// src/menus/BubbleMenu.tsx
var BubbleMenu = import_react3.default.forwardRef(
({
pluginKey,
editor,
updateDelay,
resizeDelay,
appendTo,
shouldShow = null,
getReferencedVirtualElement,
options,
children,
...restProps
}, ref) => {
const menuEl = (0, import_react3.useRef)(document.createElement("div"));
const resolvedPluginKey = (0, import_react3.useRef)(
getAutoPluginKey(pluginKey, "bubbleMenu")
).current;
useMenuElementProps(menuEl.current, restProps);
if (typeof ref === "function") {
ref(menuEl.current);
} else if (ref) {
ref.current = menuEl.current;
}
const { editor: currentEditor } = (0, import_react2.useCurrentEditor)();
const pluginEditor = editor || currentEditor;
const bubbleMenuPluginProps = {
updateDelay,
resizeDelay,
appendTo,
pluginKey: resolvedPluginKey,
shouldShow,
getReferencedVirtualElement,
options
};
const bubbleMenuPluginPropsRef = (0, import_react3.useRef)(bubbleMenuPluginProps);
bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps;
const [pluginInitialized, setPluginInitialized] = (0, import_react3.useState)(false);
const skipFirstUpdateRef = (0, import_react3.useRef)(true);
(0, import_react3.useEffect)(() => {
if (pluginEditor == null ? void 0 : pluginEditor.isDestroyed) {
return;
}
if (!pluginEditor) {
console.warn(
"BubbleMenu component is not rendered inside of an editor component or does not have editor prop."
);
return;
}
const bubbleMenuElement = menuEl.current;
bubbleMenuElement.style.visibility = "hidden";
bubbleMenuElement.style.position = "absolute";
const plugin = (0, import_extension_bubble_menu.BubbleMenuPlugin)({
...bubbleMenuPluginPropsRef.current,
editor: pluginEditor,
element: bubbleMenuElement
});
pluginEditor.registerPlugin(plugin);
const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey;
skipFirstUpdateRef.current = true;
setPluginInitialized(true);
return () => {
setPluginInitialized(false);
pluginEditor.unregisterPlugin(createdPluginKey);
window.requestAnimationFrame(() => {
if (bubbleMenuElement.parentNode) {
bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
}
});
};
}, [pluginEditor]);
(0, import_react3.useEffect)(() => {
if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {
return;
}
if (skipFirstUpdateRef.current) {
skipFirstUpdateRef.current = false;
return;
}
pluginEditor.view.dispatch(
pluginEditor.state.tr.setMeta(resolvedPluginKey, {
type: "updateOptions",
options: bubbleMenuPluginPropsRef.current
})
);
}, [
pluginInitialized,
pluginEditor,
updateDelay,
resizeDelay,
shouldShow,
options,
appendTo,
getReferencedVirtualElement,
resolvedPluginKey
]);
return (0, import_react_dom.createPortal)(children, menuEl.current);
}
);
// src/menus/FloatingMenu.tsx
var import_extension_floating_menu = require("@tiptap/extension-floating-menu");
var import_react4 = require("@tiptap/react");
var import_react5 = __toESM(require("react"), 1);
var import_react_dom2 = require("react-dom");
var FloatingMenu = import_react5.default.forwardRef(
({
pluginKey,
editor,
updateDelay,
resizeDelay,
appendTo,
shouldShow = null,
options,
children,
...restProps
}, ref) => {
const menuEl = (0, import_react5.useRef)(document.createElement("div"));
const resolvedPluginKey = (0, import_react5.useRef)(
getAutoPluginKey(pluginKey, "floatingMenu")
).current;
useMenuElementProps(menuEl.current, restProps);
if (typeof ref === "function") {
ref(menuEl.current);
} else if (ref) {
ref.current = menuEl.current;
}
const { editor: currentEditor } = (0, import_react4.useCurrentEditor)();
const pluginEditor = editor || currentEditor;
const floatingMenuPluginProps = {
updateDelay,
resizeDelay,
appendTo,
pluginKey: resolvedPluginKey,
shouldShow,
options
};
const floatingMenuPluginPropsRef = (0, import_react5.useRef)(floatingMenuPluginProps);
floatingMenuPluginPropsRef.current = floatingMenuPluginProps;
const [pluginInitialized, setPluginInitialized] = (0, import_react5.useState)(false);
const skipFirstUpdateRef = (0, import_react5.useRef)(true);
(0, import_react5.useEffect)(() => {
if (pluginEditor == null ? void 0 : pluginEditor.isDestroyed) {
return;
}
if (!pluginEditor) {
console.warn(
"FloatingMenu component is not rendered inside of an editor component or does not have editor prop."
);
return;
}
const floatingMenuElement = menuEl.current;
floatingMenuElement.style.visibility = "hidden";
floatingMenuElement.style.position = "absolute";
const plugin = (0, import_extension_floating_menu.FloatingMenuPlugin)({
...floatingMenuPluginPropsRef.current,
editor: pluginEditor,
element: floatingMenuElement
});
pluginEditor.registerPlugin(plugin);
const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey;
skipFirstUpdateRef.current = true;
setPluginInitialized(true);
return () => {
setPluginInitialized(false);
pluginEditor.unregisterPlugin(createdPluginKey);
window.requestAnimationFrame(() => {
if (floatingMenuElement.parentNode) {
floatingMenuElement.parentNode.removeChild(floatingMenuElement);
}
});
};
}, [pluginEditor]);
(0, import_react5.useEffect)(() => {
if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {
return;
}
if (skipFirstUpdateRef.current) {
skipFirstUpdateRef.current = false;
return;
}
pluginEditor.view.dispatch(
pluginEditor.state.tr.setMeta(resolvedPluginKey, {
type: "updateOptions",
options: floatingMenuPluginPropsRef.current
})
);
}, [
pluginInitialized,
pluginEditor,
updateDelay,
resizeDelay,
shouldShow,
options,
appendTo,
resolvedPluginKey
]);
return (0, import_react_dom2.createPortal)(children, menuEl.current);
}
);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BubbleMenu,
FloatingMenu
});
//# sourceMappingURL=index.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../src/menus/index.ts","../../src/menus/BubbleMenu.tsx","../../src/menus/getAutoPluginKey.ts","../../src/menus/useMenuElementProps.ts","../../src/menus/FloatingMenu.tsx"],"sourcesContent":["'use client'\n\nexport * from './BubbleMenu.js'\nexport * from './FloatingMenu.js'\n","import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'\nimport type { PluginKey } from '@tiptap/pm/state'\nimport { useCurrentEditor } from '@tiptap/react'\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getAutoPluginKey } from './getAutoPluginKey.js'\nimport { useMenuElementProps } from './useMenuElementProps.js'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type BubbleMenuProps = Optional<\n Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>,\n 'editor'\n> &\n React.HTMLAttributes<HTMLDivElement>\n\nexport const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(\n (\n {\n pluginKey,\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n getReferencedVirtualElement,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n const resolvedPluginKey = useRef<PluginKey | string>(\n getAutoPluginKey(pluginKey, 'bubbleMenu'),\n ).current\n\n useMenuElementProps(menuEl.current, restProps)\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n /**\n * The editor instance where the bubble menu plugin will be registered.\n */\n const pluginEditor = editor || currentEditor\n\n // Creating a useMemo would be more computationally expensive than just\n // re-creating this object on every render.\n const bubbleMenuPluginProps: Omit<BubbleMenuPluginProps, 'editor' | 'element'> = {\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey: resolvedPluginKey,\n shouldShow,\n getReferencedVirtualElement,\n options,\n }\n /**\n * The props for the bubble menu plugin. They are accessed inside a ref to\n * avoid running the useEffect hook and re-registering the plugin when the\n * props change.\n */\n const bubbleMenuPluginPropsRef = useRef(bubbleMenuPluginProps)\n bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps\n\n /**\n * Track whether the plugin has been initialized, so we only send updates\n * after the initial registration.\n */\n const [pluginInitialized, setPluginInitialized] = useState(false)\n\n /**\n * Track whether we need to skip the first options update dispatch.\n * This prevents unnecessary updates right after plugin initialization.\n */\n const skipFirstUpdateRef = useRef(true)\n\n useEffect(() => {\n if (pluginEditor?.isDestroyed) {\n return\n }\n\n if (!pluginEditor) {\n console.warn(\n 'BubbleMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const bubbleMenuElement = menuEl.current\n bubbleMenuElement.style.visibility = 'hidden'\n bubbleMenuElement.style.position = 'absolute'\n\n const plugin = BubbleMenuPlugin({\n ...bubbleMenuPluginPropsRef.current,\n editor: pluginEditor,\n element: bubbleMenuElement,\n })\n\n pluginEditor.registerPlugin(plugin)\n\n const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey\n\n skipFirstUpdateRef.current = true\n setPluginInitialized(true)\n\n return () => {\n setPluginInitialized(false)\n pluginEditor.unregisterPlugin(createdPluginKey)\n window.requestAnimationFrame(() => {\n if (bubbleMenuElement.parentNode) {\n bubbleMenuElement.parentNode.removeChild(bubbleMenuElement)\n }\n })\n }\n }, [pluginEditor])\n\n /**\n * Update the plugin options when props change after the plugin has been initialized.\n * This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.\n */\n useEffect(() => {\n if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {\n return\n }\n\n // Skip the first update right after initialization since the plugin was just created with these options\n if (skipFirstUpdateRef.current) {\n skipFirstUpdateRef.current = false\n return\n }\n\n pluginEditor.view.dispatch(\n pluginEditor.state.tr.setMeta(resolvedPluginKey, {\n type: 'updateOptions',\n options: bubbleMenuPluginPropsRef.current,\n }),\n )\n }, [\n pluginInitialized,\n pluginEditor,\n updateDelay,\n resizeDelay,\n shouldShow,\n options,\n appendTo,\n getReferencedVirtualElement,\n resolvedPluginKey,\n ])\n\n return createPortal(children, menuEl.current)\n },\n)\n","import { PluginKey } from '@tiptap/pm/state'\n\nexport function getAutoPluginKey(pluginKey: PluginKey | string | undefined, defaultName: string) {\n return pluginKey ?? new PluginKey(defaultName)\n}\n","import type { CSSProperties, HTMLAttributes } from 'react'\nimport { useEffect, useLayoutEffect, useRef } from 'react'\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\ntype MenuElementProps = HTMLAttributes<HTMLDivElement>\ntype MenuSyntheticEvent = Event & {\n nativeEvent: Event\n currentTarget: HTMLDivElement\n target: EventTarget | null\n persist: () => void\n isDefaultPrevented: () => boolean\n isPropagationStopped: () => boolean\n}\ntype MenuEventListener = (event: MenuSyntheticEvent) => void\ntype MenuNativeListener = (event: Event) => void\ntype MenuEventListenerOptions = {\n capture?: boolean\n}\n\ntype EventListenerEntry = {\n eventName: string\n listener: MenuNativeListener\n options?: MenuEventListenerOptions\n}\n\nconst PLUGIN_MANAGED_STYLE_PROPERTIES = new Set([\n 'left',\n 'opacity',\n 'position',\n 'top',\n 'visibility',\n 'width',\n])\nconst UNITLESS_STYLE_PROPERTIES = new Set([\n 'animationIterationCount',\n 'aspectRatio',\n 'borderImageOutset',\n 'borderImageSlice',\n 'borderImageWidth',\n 'columnCount',\n 'columns',\n 'fillOpacity',\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'fontWeight',\n 'gridArea',\n 'gridColumn',\n 'gridColumnEnd',\n 'gridColumnStart',\n 'gridRow',\n 'gridRowEnd',\n 'gridRowStart',\n 'lineClamp',\n 'lineHeight',\n 'opacity',\n 'order',\n 'orphans',\n 'scale',\n 'stopOpacity',\n 'strokeDasharray',\n 'strokeDashoffset',\n 'strokeMiterlimit',\n 'strokeOpacity',\n 'strokeWidth',\n 'tabSize',\n 'widows',\n 'zIndex',\n 'zoom',\n])\nconst ATTRIBUTE_EXCLUSIONS = new Set(['children', 'className', 'style'])\nconst DIRECT_PROPERTY_KEYS = new Set(['tabIndex'])\nconst FORWARDED_ATTRIBUTE_KEYS = new Set([\n 'accessKey',\n 'autoCapitalize',\n 'contentEditable',\n 'contextMenu',\n 'dir',\n 'draggable',\n 'enterKeyHint',\n 'hidden',\n 'id',\n 'lang',\n 'nonce',\n 'role',\n 'slot',\n 'spellCheck',\n 'tabIndex',\n 'title',\n 'translate',\n])\nconst SPECIAL_EVENT_NAMES: Record<string, string> = {\n Blur: 'focusout',\n DoubleClick: 'dblclick',\n Focus: 'focusin',\n MouseEnter: 'mouseenter',\n MouseLeave: 'mouseleave',\n}\n\nfunction isEventProp(key: string, value: unknown): value is MenuEventListener {\n return /^on[A-Z]/.test(key) && typeof value === 'function'\n}\n\nfunction toAttributeName(key: string) {\n if (key.startsWith('aria-') || key.startsWith('data-')) {\n return key\n }\n\n return key\n}\n\nfunction isForwardedAttributeKey(key: string) {\n return key.startsWith('aria-') || key.startsWith('data-') || FORWARDED_ATTRIBUTE_KEYS.has(key)\n}\n\nfunction toStylePropertyName(key: string) {\n if (key.startsWith('--')) {\n return key\n }\n\n return key.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)\n}\n\nfunction toEventConfig(key: string) {\n const useCapture = key.endsWith('Capture')\n const baseKey = useCapture ? key.slice(0, -7) : key\n const reactEventName = baseKey.slice(2)\n const eventName = SPECIAL_EVENT_NAMES[reactEventName] ?? reactEventName.toLowerCase()\n\n return {\n eventName,\n options: useCapture ? { capture: true } : undefined,\n }\n}\n\nfunction createSyntheticEvent(element: HTMLDivElement, nativeEvent: Event): MenuSyntheticEvent {\n let defaultPrevented = nativeEvent.defaultPrevented\n let propagationStopped = false\n const syntheticEvent = Object.create(nativeEvent)\n\n Object.defineProperties(syntheticEvent, {\n nativeEvent: { value: nativeEvent },\n currentTarget: { value: element },\n target: { value: nativeEvent.target },\n persist: { value: () => undefined },\n isDefaultPrevented: { value: () => defaultPrevented },\n isPropagationStopped: { value: () => propagationStopped },\n preventDefault: {\n value: () => {\n defaultPrevented = true\n nativeEvent.preventDefault()\n },\n },\n stopPropagation: {\n value: () => {\n propagationStopped = true\n nativeEvent.stopPropagation()\n },\n },\n })\n\n return syntheticEvent as MenuSyntheticEvent\n}\n\nfunction isDirectPropertyKey(key: string) {\n return DIRECT_PROPERTY_KEYS.has(key)\n}\n\nfunction setDirectProperty(element: HTMLDivElement, key: string, value: unknown) {\n if (key === 'tabIndex') {\n element.tabIndex = Number(value)\n return\n }\n\n ;(element as unknown as Record<string, unknown>)[key] = value\n}\n\nfunction clearDirectProperty(element: HTMLDivElement, key: string) {\n if (key === 'tabIndex') {\n element.removeAttribute('tabindex')\n return\n }\n\n const propertyValue = (element as unknown as Record<string, unknown>)[key]\n\n if (typeof propertyValue === 'boolean') {\n ;(element as unknown as Record<string, unknown>)[key] = false\n return\n }\n\n if (typeof propertyValue === 'number') {\n ;(element as unknown as Record<string, unknown>)[key] = 0\n return\n }\n\n ;(element as unknown as Record<string, unknown>)[key] = ''\n}\n\nfunction toStyleValue(styleName: string, value: string | number) {\n if (\n typeof value !== 'number' ||\n value === 0 ||\n styleName.startsWith('--') ||\n UNITLESS_STYLE_PROPERTIES.has(styleName)\n ) {\n return String(value)\n }\n\n return `${value}px`\n}\n\nfunction removeStyleProperty(element: HTMLDivElement, styleName: string) {\n if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {\n return\n }\n\n element.style.removeProperty(toStylePropertyName(styleName))\n}\n\nfunction applyStyleProperty(element: HTMLDivElement, styleName: string, value: string | number) {\n if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {\n return\n }\n\n element.style.setProperty(toStylePropertyName(styleName), toStyleValue(styleName, value))\n}\n\nfunction syncAttributes(\n element: HTMLDivElement,\n prevProps: MenuElementProps,\n nextProps: MenuElementProps,\n) {\n const allKeys = new Set([...Object.keys(prevProps), ...Object.keys(nextProps)])\n\n allKeys.forEach(key => {\n if (\n ATTRIBUTE_EXCLUSIONS.has(key) ||\n !isForwardedAttributeKey(key) ||\n isEventProp(key, prevProps[key as keyof MenuElementProps]) ||\n isEventProp(key, nextProps[key as keyof MenuElementProps])\n ) {\n return\n }\n\n const prevValue = prevProps[key as keyof MenuElementProps]\n const nextValue = nextProps[key as keyof MenuElementProps]\n\n if (prevValue === nextValue) {\n return\n }\n\n const attributeName = toAttributeName(key)\n\n if (nextValue == null || nextValue === false) {\n if (isDirectPropertyKey(key)) {\n clearDirectProperty(element, key)\n }\n\n element.removeAttribute(attributeName)\n return\n }\n\n if (nextValue === true) {\n if (isDirectPropertyKey(key)) {\n setDirectProperty(element, key, true)\n }\n\n element.setAttribute(attributeName, '')\n return\n }\n\n if (isDirectPropertyKey(key)) {\n setDirectProperty(element, key, nextValue)\n return\n }\n\n element.setAttribute(attributeName, String(nextValue))\n })\n}\n\nfunction syncClassName(element: HTMLDivElement, prevClassName?: string, nextClassName?: string) {\n if (prevClassName === nextClassName) {\n return\n }\n\n if (nextClassName) {\n element.className = nextClassName\n return\n }\n\n element.removeAttribute('class')\n}\n\nfunction syncStyles(\n element: HTMLDivElement,\n prevStyle: CSSProperties | undefined,\n nextStyle: CSSProperties | undefined,\n) {\n const previousStyle = prevStyle ?? {}\n const currentStyle = nextStyle ?? {}\n const allStyleNames = new Set([...Object.keys(previousStyle), ...Object.keys(currentStyle)])\n\n allStyleNames.forEach(styleName => {\n const prevValue = previousStyle[styleName as keyof CSSProperties]\n const nextValue = currentStyle[styleName as keyof CSSProperties]\n\n if (prevValue === nextValue) {\n return\n }\n\n if (nextValue == null) {\n removeStyleProperty(element, styleName)\n return\n }\n\n applyStyleProperty(element, styleName, nextValue as string | number)\n })\n}\n\nfunction syncEventListeners(\n element: HTMLDivElement,\n prevListeners: EventListenerEntry[],\n nextProps: MenuElementProps,\n) {\n prevListeners.forEach(({ eventName, listener, options }) => {\n element.removeEventListener(eventName, listener, options)\n })\n\n const nextListeners: EventListenerEntry[] = []\n\n Object.entries(nextProps).forEach(([key, value]) => {\n if (!isEventProp(key, value)) {\n return\n }\n\n const { eventName, options } = toEventConfig(key)\n const listener: MenuNativeListener = event => {\n value(createSyntheticEvent(element, event))\n }\n\n element.addEventListener(eventName, listener, options)\n nextListeners.push({ eventName, listener, options })\n })\n\n return nextListeners\n}\n\nexport function useMenuElementProps(element: HTMLDivElement, props: MenuElementProps) {\n const previousPropsRef = useRef<MenuElementProps>({})\n const listenersRef = useRef<EventListenerEntry[]>([])\n\n useIsomorphicLayoutEffect(() => {\n const previousProps = previousPropsRef.current\n\n syncClassName(element, previousProps.className, props.className)\n syncStyles(element, previousProps.style, props.style)\n syncAttributes(element, previousProps, props)\n listenersRef.current = syncEventListeners(element, listenersRef.current, props)\n previousPropsRef.current = props\n\n return () => {\n listenersRef.current.forEach(({ eventName, listener, options }) => {\n element.removeEventListener(eventName, listener, options)\n })\n listenersRef.current = []\n }\n }, [element, props])\n}\n","import type { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'\nimport { FloatingMenuPlugin } from '@tiptap/extension-floating-menu'\nimport type { PluginKey } from '@tiptap/pm/state'\nimport { useCurrentEditor } from '@tiptap/react'\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getAutoPluginKey } from './getAutoPluginKey.js'\nimport { useMenuElementProps } from './useMenuElementProps.js'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type FloatingMenuProps = Omit<\n Optional<FloatingMenuPluginProps, 'pluginKey'>,\n 'element' | 'editor'\n> & {\n editor: FloatingMenuPluginProps['editor'] | null\n options?: FloatingMenuPluginProps['options']\n} & React.HTMLAttributes<HTMLDivElement>\n\nexport const FloatingMenu = React.forwardRef<HTMLDivElement, FloatingMenuProps>(\n (\n {\n pluginKey,\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n const resolvedPluginKey = useRef<PluginKey | string>(\n getAutoPluginKey(pluginKey, 'floatingMenu'),\n ).current\n\n useMenuElementProps(menuEl.current, restProps)\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n /**\n * The editor instance where the floating menu plugin will be registered.\n */\n const pluginEditor = editor || currentEditor\n\n // Creating a useMemo would be more computationally expensive than just\n // re-creating this object on every render.\n const floatingMenuPluginProps: Omit<FloatingMenuPluginProps, 'editor' | 'element'> = {\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey: resolvedPluginKey,\n shouldShow,\n options,\n }\n\n /**\n * The props for the floating menu plugin. They are accessed inside a ref to\n * avoid running the useEffect hook and re-registering the plugin when the\n * props change.\n */\n const floatingMenuPluginPropsRef = useRef(floatingMenuPluginProps)\n floatingMenuPluginPropsRef.current = floatingMenuPluginProps\n\n /**\n * Track whether the plugin has been initialized, so we only send updates\n * after the initial registration.\n */\n const [pluginInitialized, setPluginInitialized] = useState(false)\n\n /**\n * Track whether we need to skip the first options update dispatch.\n * This prevents unnecessary updates right after plugin initialization.\n */\n const skipFirstUpdateRef = useRef(true)\n\n useEffect(() => {\n if (pluginEditor?.isDestroyed) {\n return\n }\n\n if (!pluginEditor) {\n console.warn(\n 'FloatingMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const floatingMenuElement = menuEl.current\n floatingMenuElement.style.visibility = 'hidden'\n floatingMenuElement.style.position = 'absolute'\n\n const plugin = FloatingMenuPlugin({\n ...floatingMenuPluginPropsRef.current,\n editor: pluginEditor,\n element: floatingMenuElement,\n })\n\n pluginEditor.registerPlugin(plugin)\n\n const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey\n\n skipFirstUpdateRef.current = true\n setPluginInitialized(true)\n\n return () => {\n setPluginInitialized(false)\n pluginEditor.unregisterPlugin(createdPluginKey)\n window.requestAnimationFrame(() => {\n if (floatingMenuElement.parentNode) {\n floatingMenuElement.parentNode.removeChild(floatingMenuElement)\n }\n })\n }\n }, [pluginEditor])\n\n /**\n * Update the plugin options when props change after the plugin has been initialized.\n * This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.\n */\n useEffect(() => {\n if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {\n return\n }\n\n // Skip the first update right after initialization since the plugin was just created with these options\n if (skipFirstUpdateRef.current) {\n skipFirstUpdateRef.current = false\n return\n }\n\n pluginEditor.view.dispatch(\n pluginEditor.state.tr.setMeta(resolvedPluginKey, {\n type: 'updateOptions',\n options: floatingMenuPluginPropsRef.current,\n }),\n )\n }, [\n pluginInitialized,\n pluginEditor,\n updateDelay,\n resizeDelay,\n shouldShow,\n options,\n appendTo,\n resolvedPluginKey,\n ])\n\n return createPortal(children, menuEl.current)\n },\n)\n"],"mappings":"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","names":["import_react","React","import_react","import_react_dom","React"]}
{"version":3,"file":"index.cjs","names":["PluginKey","useLayoutEffect","useEffect","React","React"],"sources":["../../src/menus/getAutoPluginKey.ts","../../src/menus/useMenuElementProps.ts","../../src/menus/BubbleMenu.tsx","../../src/menus/FloatingMenu.tsx"],"sourcesContent":["import { PluginKey } from '@tiptap/pm/state'\n\nexport function getAutoPluginKey(pluginKey: PluginKey | string | undefined, defaultName: string) {\n return pluginKey ?? new PluginKey(defaultName)\n}\n","import type { CSSProperties, HTMLAttributes } from 'react'\nimport { useEffect, useLayoutEffect, useRef } from 'react'\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\ntype MenuElementProps = HTMLAttributes<HTMLDivElement>\ntype MenuSyntheticEvent = Event & {\n nativeEvent: Event\n currentTarget: HTMLDivElement\n target: EventTarget | null\n persist: () => void\n isDefaultPrevented: () => boolean\n isPropagationStopped: () => boolean\n}\ntype MenuEventListener = (event: MenuSyntheticEvent) => void\ntype MenuNativeListener = (event: Event) => void\ntype MenuEventListenerOptions = {\n capture?: boolean\n}\n\ntype EventListenerEntry = {\n eventName: string\n listener: MenuNativeListener\n options?: MenuEventListenerOptions\n}\n\nconst PLUGIN_MANAGED_STYLE_PROPERTIES = new Set([\n 'left',\n 'opacity',\n 'position',\n 'top',\n 'visibility',\n 'width',\n])\nconst UNITLESS_STYLE_PROPERTIES = new Set([\n 'animationIterationCount',\n 'aspectRatio',\n 'borderImageOutset',\n 'borderImageSlice',\n 'borderImageWidth',\n 'columnCount',\n 'columns',\n 'fillOpacity',\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'fontWeight',\n 'gridArea',\n 'gridColumn',\n 'gridColumnEnd',\n 'gridColumnStart',\n 'gridRow',\n 'gridRowEnd',\n 'gridRowStart',\n 'lineClamp',\n 'lineHeight',\n 'opacity',\n 'order',\n 'orphans',\n 'scale',\n 'stopOpacity',\n 'strokeDasharray',\n 'strokeDashoffset',\n 'strokeMiterlimit',\n 'strokeOpacity',\n 'strokeWidth',\n 'tabSize',\n 'widows',\n 'zIndex',\n 'zoom',\n])\nconst ATTRIBUTE_EXCLUSIONS = new Set(['children', 'className', 'style'])\nconst DIRECT_PROPERTY_KEYS = new Set(['tabIndex'])\nconst FORWARDED_ATTRIBUTE_KEYS = new Set([\n 'accessKey',\n 'autoCapitalize',\n 'contentEditable',\n 'contextMenu',\n 'dir',\n 'draggable',\n 'enterKeyHint',\n 'hidden',\n 'id',\n 'lang',\n 'nonce',\n 'role',\n 'slot',\n 'spellCheck',\n 'tabIndex',\n 'title',\n 'translate',\n])\nconst SPECIAL_EVENT_NAMES: Record<string, string> = {\n Blur: 'focusout',\n DoubleClick: 'dblclick',\n Focus: 'focusin',\n MouseEnter: 'mouseenter',\n MouseLeave: 'mouseleave',\n}\n\nfunction isEventProp(key: string, value: unknown): value is MenuEventListener {\n return /^on[A-Z]/.test(key) && typeof value === 'function'\n}\n\nfunction toAttributeName(key: string) {\n if (key.startsWith('aria-') || key.startsWith('data-')) {\n return key\n }\n\n return key\n}\n\nfunction isForwardedAttributeKey(key: string) {\n return key.startsWith('aria-') || key.startsWith('data-') || FORWARDED_ATTRIBUTE_KEYS.has(key)\n}\n\nfunction toStylePropertyName(key: string) {\n if (key.startsWith('--')) {\n return key\n }\n\n return key.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)\n}\n\nfunction toEventConfig(key: string) {\n const useCapture = key.endsWith('Capture')\n const baseKey = useCapture ? key.slice(0, -7) : key\n const reactEventName = baseKey.slice(2)\n const eventName = SPECIAL_EVENT_NAMES[reactEventName] ?? reactEventName.toLowerCase()\n\n return {\n eventName,\n options: useCapture ? { capture: true } : undefined,\n }\n}\n\nfunction createSyntheticEvent(element: HTMLDivElement, nativeEvent: Event): MenuSyntheticEvent {\n let defaultPrevented = nativeEvent.defaultPrevented\n let propagationStopped = false\n const syntheticEvent = Object.create(nativeEvent)\n\n Object.defineProperties(syntheticEvent, {\n nativeEvent: { value: nativeEvent },\n currentTarget: { value: element },\n target: { value: nativeEvent.target },\n persist: { value: () => undefined },\n isDefaultPrevented: { value: () => defaultPrevented },\n isPropagationStopped: { value: () => propagationStopped },\n preventDefault: {\n value: () => {\n defaultPrevented = true\n nativeEvent.preventDefault()\n },\n },\n stopPropagation: {\n value: () => {\n propagationStopped = true\n nativeEvent.stopPropagation()\n },\n },\n })\n\n return syntheticEvent as MenuSyntheticEvent\n}\n\nfunction isDirectPropertyKey(key: string) {\n return DIRECT_PROPERTY_KEYS.has(key)\n}\n\nfunction setDirectProperty(element: HTMLDivElement, key: string, value: unknown) {\n if (key === 'tabIndex') {\n element.tabIndex = Number(value)\n return\n }\n\n ;(element as unknown as Record<string, unknown>)[key] = value\n}\n\nfunction clearDirectProperty(element: HTMLDivElement, key: string) {\n if (key === 'tabIndex') {\n element.removeAttribute('tabindex')\n return\n }\n\n const propertyValue = (element as unknown as Record<string, unknown>)[key]\n\n if (typeof propertyValue === 'boolean') {\n ;(element as unknown as Record<string, unknown>)[key] = false\n return\n }\n\n if (typeof propertyValue === 'number') {\n ;(element as unknown as Record<string, unknown>)[key] = 0\n return\n }\n\n ;(element as unknown as Record<string, unknown>)[key] = ''\n}\n\nfunction toStyleValue(styleName: string, value: string | number) {\n if (\n typeof value !== 'number' ||\n value === 0 ||\n styleName.startsWith('--') ||\n UNITLESS_STYLE_PROPERTIES.has(styleName)\n ) {\n return String(value)\n }\n\n return `${value}px`\n}\n\nfunction removeStyleProperty(element: HTMLDivElement, styleName: string) {\n if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {\n return\n }\n\n element.style.removeProperty(toStylePropertyName(styleName))\n}\n\nfunction applyStyleProperty(element: HTMLDivElement, styleName: string, value: string | number) {\n if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {\n return\n }\n\n element.style.setProperty(toStylePropertyName(styleName), toStyleValue(styleName, value))\n}\n\nfunction syncAttributes(\n element: HTMLDivElement,\n prevProps: MenuElementProps,\n nextProps: MenuElementProps,\n) {\n const allKeys = new Set([...Object.keys(prevProps), ...Object.keys(nextProps)])\n\n allKeys.forEach(key => {\n if (\n ATTRIBUTE_EXCLUSIONS.has(key) ||\n !isForwardedAttributeKey(key) ||\n isEventProp(key, prevProps[key as keyof MenuElementProps]) ||\n isEventProp(key, nextProps[key as keyof MenuElementProps])\n ) {\n return\n }\n\n const prevValue = prevProps[key as keyof MenuElementProps]\n const nextValue = nextProps[key as keyof MenuElementProps]\n\n if (prevValue === nextValue) {\n return\n }\n\n const attributeName = toAttributeName(key)\n\n if (nextValue == null || nextValue === false) {\n if (isDirectPropertyKey(key)) {\n clearDirectProperty(element, key)\n }\n\n element.removeAttribute(attributeName)\n return\n }\n\n if (nextValue === true) {\n if (isDirectPropertyKey(key)) {\n setDirectProperty(element, key, true)\n }\n\n element.setAttribute(attributeName, '')\n return\n }\n\n if (isDirectPropertyKey(key)) {\n setDirectProperty(element, key, nextValue)\n return\n }\n\n element.setAttribute(attributeName, String(nextValue))\n })\n}\n\nfunction syncClassName(element: HTMLDivElement, prevClassName?: string, nextClassName?: string) {\n if (prevClassName === nextClassName) {\n return\n }\n\n if (nextClassName) {\n element.className = nextClassName\n return\n }\n\n element.removeAttribute('class')\n}\n\nfunction syncStyles(\n element: HTMLDivElement,\n prevStyle: CSSProperties | undefined,\n nextStyle: CSSProperties | undefined,\n) {\n const previousStyle = prevStyle ?? {}\n const currentStyle = nextStyle ?? {}\n const allStyleNames = new Set([...Object.keys(previousStyle), ...Object.keys(currentStyle)])\n\n allStyleNames.forEach(styleName => {\n const prevValue = previousStyle[styleName as keyof CSSProperties]\n const nextValue = currentStyle[styleName as keyof CSSProperties]\n\n if (prevValue === nextValue) {\n return\n }\n\n if (nextValue == null) {\n removeStyleProperty(element, styleName)\n return\n }\n\n applyStyleProperty(element, styleName, nextValue as string | number)\n })\n}\n\nfunction syncEventListeners(\n element: HTMLDivElement,\n prevListeners: EventListenerEntry[],\n nextProps: MenuElementProps,\n) {\n prevListeners.forEach(({ eventName, listener, options }) => {\n element.removeEventListener(eventName, listener, options)\n })\n\n const nextListeners: EventListenerEntry[] = []\n\n Object.entries(nextProps).forEach(([key, value]) => {\n if (!isEventProp(key, value)) {\n return\n }\n\n const { eventName, options } = toEventConfig(key)\n const listener: MenuNativeListener = event => {\n value(createSyntheticEvent(element, event))\n }\n\n element.addEventListener(eventName, listener, options)\n nextListeners.push({ eventName, listener, options })\n })\n\n return nextListeners\n}\n\nexport function useMenuElementProps(element: HTMLDivElement, props: MenuElementProps) {\n const previousPropsRef = useRef<MenuElementProps>({})\n const listenersRef = useRef<EventListenerEntry[]>([])\n\n useIsomorphicLayoutEffect(() => {\n const previousProps = previousPropsRef.current\n\n syncClassName(element, previousProps.className, props.className)\n syncStyles(element, previousProps.style, props.style)\n syncAttributes(element, previousProps, props)\n listenersRef.current = syncEventListeners(element, listenersRef.current, props)\n previousPropsRef.current = props\n\n return () => {\n listenersRef.current.forEach(({ eventName, listener, options }) => {\n element.removeEventListener(eventName, listener, options)\n })\n listenersRef.current = []\n }\n }, [element, props])\n}\n","import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'\nimport type { PluginKey } from '@tiptap/pm/state'\nimport { useCurrentEditor } from '@tiptap/react'\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getAutoPluginKey } from './getAutoPluginKey.js'\nimport { useMenuElementProps } from './useMenuElementProps.js'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type BubbleMenuProps = Optional<\n Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>,\n 'editor'\n> &\n React.HTMLAttributes<HTMLDivElement>\n\nexport const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(\n (\n {\n pluginKey,\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n getReferencedVirtualElement,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n const resolvedPluginKey = useRef<PluginKey | string>(\n getAutoPluginKey(pluginKey, 'bubbleMenu'),\n ).current\n\n useMenuElementProps(menuEl.current, restProps)\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n /**\n * The editor instance where the bubble menu plugin will be registered.\n */\n const pluginEditor = editor || currentEditor\n\n // Creating a useMemo would be more computationally expensive than just\n // re-creating this object on every render.\n const bubbleMenuPluginProps: Omit<BubbleMenuPluginProps, 'editor' | 'element'> = {\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey: resolvedPluginKey,\n shouldShow,\n getReferencedVirtualElement,\n options,\n }\n /**\n * The props for the bubble menu plugin. They are accessed inside a ref to\n * avoid running the useEffect hook and re-registering the plugin when the\n * props change.\n */\n const bubbleMenuPluginPropsRef = useRef(bubbleMenuPluginProps)\n bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps\n\n /**\n * Track whether the plugin has been initialized, so we only send updates\n * after the initial registration.\n */\n const [pluginInitialized, setPluginInitialized] = useState(false)\n\n /**\n * Track whether we need to skip the first options update dispatch.\n * This prevents unnecessary updates right after plugin initialization.\n */\n const skipFirstUpdateRef = useRef(true)\n\n useEffect(() => {\n if (pluginEditor?.isDestroyed) {\n return\n }\n\n if (!pluginEditor) {\n console.warn(\n 'BubbleMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const bubbleMenuElement = menuEl.current\n bubbleMenuElement.style.visibility = 'hidden'\n bubbleMenuElement.style.position = 'absolute'\n\n const plugin = BubbleMenuPlugin({\n ...bubbleMenuPluginPropsRef.current,\n editor: pluginEditor,\n element: bubbleMenuElement,\n })\n\n pluginEditor.registerPlugin(plugin)\n\n const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey\n\n skipFirstUpdateRef.current = true\n setPluginInitialized(true)\n\n return () => {\n setPluginInitialized(false)\n pluginEditor.unregisterPlugin(createdPluginKey)\n window.requestAnimationFrame(() => {\n if (bubbleMenuElement.parentNode) {\n bubbleMenuElement.parentNode.removeChild(bubbleMenuElement)\n }\n })\n }\n }, [pluginEditor])\n\n /**\n * Update the plugin options when props change after the plugin has been initialized.\n * This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.\n */\n useEffect(() => {\n if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {\n return\n }\n\n // Skip the first update right after initialization since the plugin was just created with these options\n if (skipFirstUpdateRef.current) {\n skipFirstUpdateRef.current = false\n return\n }\n\n pluginEditor.view.dispatch(\n pluginEditor.state.tr.setMeta(resolvedPluginKey, {\n type: 'updateOptions',\n options: bubbleMenuPluginPropsRef.current,\n }),\n )\n }, [\n pluginInitialized,\n pluginEditor,\n updateDelay,\n resizeDelay,\n shouldShow,\n options,\n appendTo,\n getReferencedVirtualElement,\n resolvedPluginKey,\n ])\n\n return createPortal(children, menuEl.current)\n },\n)\n","import type { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'\nimport { FloatingMenuPlugin } from '@tiptap/extension-floating-menu'\nimport type { PluginKey } from '@tiptap/pm/state'\nimport { useCurrentEditor } from '@tiptap/react'\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getAutoPluginKey } from './getAutoPluginKey.js'\nimport { useMenuElementProps } from './useMenuElementProps.js'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type FloatingMenuProps = Omit<\n Optional<FloatingMenuPluginProps, 'pluginKey'>,\n 'element' | 'editor'\n> & {\n editor: FloatingMenuPluginProps['editor'] | null\n options?: FloatingMenuPluginProps['options']\n} & React.HTMLAttributes<HTMLDivElement>\n\nexport const FloatingMenu = React.forwardRef<HTMLDivElement, FloatingMenuProps>(\n (\n {\n pluginKey,\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n const resolvedPluginKey = useRef<PluginKey | string>(\n getAutoPluginKey(pluginKey, 'floatingMenu'),\n ).current\n\n useMenuElementProps(menuEl.current, restProps)\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n /**\n * The editor instance where the floating menu plugin will be registered.\n */\n const pluginEditor = editor || currentEditor\n\n // Creating a useMemo would be more computationally expensive than just\n // re-creating this object on every render.\n const floatingMenuPluginProps: Omit<FloatingMenuPluginProps, 'editor' | 'element'> = {\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey: resolvedPluginKey,\n shouldShow,\n options,\n }\n\n /**\n * The props for the floating menu plugin. They are accessed inside a ref to\n * avoid running the useEffect hook and re-registering the plugin when the\n * props change.\n */\n const floatingMenuPluginPropsRef = useRef(floatingMenuPluginProps)\n floatingMenuPluginPropsRef.current = floatingMenuPluginProps\n\n /**\n * Track whether the plugin has been initialized, so we only send updates\n * after the initial registration.\n */\n const [pluginInitialized, setPluginInitialized] = useState(false)\n\n /**\n * Track whether we need to skip the first options update dispatch.\n * This prevents unnecessary updates right after plugin initialization.\n */\n const skipFirstUpdateRef = useRef(true)\n\n useEffect(() => {\n if (pluginEditor?.isDestroyed) {\n return\n }\n\n if (!pluginEditor) {\n console.warn(\n 'FloatingMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const floatingMenuElement = menuEl.current\n floatingMenuElement.style.visibility = 'hidden'\n floatingMenuElement.style.position = 'absolute'\n\n const plugin = FloatingMenuPlugin({\n ...floatingMenuPluginPropsRef.current,\n editor: pluginEditor,\n element: floatingMenuElement,\n })\n\n pluginEditor.registerPlugin(plugin)\n\n const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey\n\n skipFirstUpdateRef.current = true\n setPluginInitialized(true)\n\n return () => {\n setPluginInitialized(false)\n pluginEditor.unregisterPlugin(createdPluginKey)\n window.requestAnimationFrame(() => {\n if (floatingMenuElement.parentNode) {\n floatingMenuElement.parentNode.removeChild(floatingMenuElement)\n }\n })\n }\n }, [pluginEditor])\n\n /**\n * Update the plugin options when props change after the plugin has been initialized.\n * This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.\n */\n useEffect(() => {\n if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {\n return\n }\n\n // Skip the first update right after initialization since the plugin was just created with these options\n if (skipFirstUpdateRef.current) {\n skipFirstUpdateRef.current = false\n return\n }\n\n pluginEditor.view.dispatch(\n pluginEditor.state.tr.setMeta(resolvedPluginKey, {\n type: 'updateOptions',\n options: floatingMenuPluginPropsRef.current,\n }),\n )\n }, [\n pluginInitialized,\n pluginEditor,\n updateDelay,\n resizeDelay,\n shouldShow,\n options,\n appendTo,\n resolvedPluginKey,\n ])\n\n return createPortal(children, menuEl.current)\n },\n)\n"],"mappings":"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"}

@@ -1,19 +0,21 @@

import { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
import React from 'react';
import { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu';
import { BubbleMenuPluginProps } from "@tiptap/extension-bubble-menu";
import React from "react";
import { FloatingMenuPluginProps } from "@tiptap/extension-floating-menu";
//#region src/menus/BubbleMenu.d.ts
type Optional$1<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
type BubbleMenuProps = Optional$1<Omit<Optional$1<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> & React.HTMLAttributes<HTMLDivElement>;
declare const BubbleMenu: React.ForwardRefExoticComponent<Pick<Partial<Omit<Optional$1<BubbleMenuPluginProps, "pluginKey">, "element">>, "editor"> & Omit<Omit<Optional$1<BubbleMenuPluginProps, "pluginKey">, "element">, "editor"> & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
//#endregion
//#region src/menus/FloatingMenu.d.ts
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {
editor: FloatingMenuPluginProps['editor'] | null;
options?: FloatingMenuPluginProps['options'];
editor: FloatingMenuPluginProps['editor'] | null;
options?: FloatingMenuPluginProps['options'];
} & React.HTMLAttributes<HTMLDivElement>;
declare const FloatingMenu: React.ForwardRefExoticComponent<Omit<Optional<FloatingMenuPluginProps, "pluginKey">, "editor" | "element"> & {
editor: FloatingMenuPluginProps["editor"] | null;
options?: FloatingMenuPluginProps["options"];
editor: FloatingMenuPluginProps["editor"] | null;
options?: FloatingMenuPluginProps["options"];
} & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
export { BubbleMenu, type BubbleMenuProps, FloatingMenu, type FloatingMenuProps };
//#endregion
export { BubbleMenu, BubbleMenuProps, FloatingMenu, FloatingMenuProps };
//# sourceMappingURL=index.d.cts.map

@@ -1,19 +0,21 @@

import { BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu';
import React from 'react';
import { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu';
import { BubbleMenuPluginProps } from "@tiptap/extension-bubble-menu";
import React from "react";
import { FloatingMenuPluginProps } from "@tiptap/extension-floating-menu";
//#region src/menus/BubbleMenu.d.ts
type Optional$1<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
type BubbleMenuProps = Optional$1<Omit<Optional$1<BubbleMenuPluginProps, 'pluginKey'>, 'element'>, 'editor'> & React.HTMLAttributes<HTMLDivElement>;
declare const BubbleMenu: React.ForwardRefExoticComponent<Pick<Partial<Omit<Optional$1<BubbleMenuPluginProps, "pluginKey">, "element">>, "editor"> & Omit<Omit<Optional$1<BubbleMenuPluginProps, "pluginKey">, "element">, "editor"> & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
//#endregion
//#region src/menus/FloatingMenu.d.ts
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element' | 'editor'> & {
editor: FloatingMenuPluginProps['editor'] | null;
options?: FloatingMenuPluginProps['options'];
editor: FloatingMenuPluginProps['editor'] | null;
options?: FloatingMenuPluginProps['options'];
} & React.HTMLAttributes<HTMLDivElement>;
declare const FloatingMenu: React.ForwardRefExoticComponent<Omit<Optional<FloatingMenuPluginProps, "pluginKey">, "editor" | "element"> & {
editor: FloatingMenuPluginProps["editor"] | null;
options?: FloatingMenuPluginProps["options"];
editor: FloatingMenuPluginProps["editor"] | null;
options?: FloatingMenuPluginProps["options"];
} & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
export { BubbleMenu, type BubbleMenuProps, FloatingMenu, type FloatingMenuProps };
//#endregion
export { BubbleMenu, BubbleMenuProps, FloatingMenu, FloatingMenuProps };
//# sourceMappingURL=index.d.ts.map
"use client";
// src/menus/BubbleMenu.tsx
import { BubbleMenuPlugin } from "@tiptap/extension-bubble-menu";
import { useCurrentEditor } from "@tiptap/react";
import React, { useEffect as useEffect2, useRef as useRef2, useState } from "react";
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
// src/menus/getAutoPluginKey.ts
import { PluginKey } from "@tiptap/pm/state";
import { FloatingMenuPlugin } from "@tiptap/extension-floating-menu";
//#region src/menus/getAutoPluginKey.ts
function getAutoPluginKey(pluginKey, defaultName) {
return pluginKey != null ? pluginKey : new PluginKey(defaultName);
return pluginKey !== null && pluginKey !== void 0 ? pluginKey : new PluginKey(defaultName);
}
// src/menus/useMenuElementProps.ts
import { useEffect, useLayoutEffect, useRef } from "react";
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
var PLUGIN_MANAGED_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
"left",
"opacity",
"position",
"top",
"visibility",
"width"
//#endregion
//#region src/menus/useMenuElementProps.ts
const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
const PLUGIN_MANAGED_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
"left",
"opacity",
"position",
"top",
"visibility",
"width"
]);
var UNITLESS_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
"animationIterationCount",
"aspectRatio",
"borderImageOutset",
"borderImageSlice",
"borderImageWidth",
"columnCount",
"columns",
"fillOpacity",
"flex",
"flexGrow",
"flexShrink",
"fontWeight",
"gridArea",
"gridColumn",
"gridColumnEnd",
"gridColumnStart",
"gridRow",
"gridRowEnd",
"gridRowStart",
"lineClamp",
"lineHeight",
"opacity",
"order",
"orphans",
"scale",
"stopOpacity",
"strokeDasharray",
"strokeDashoffset",
"strokeMiterlimit",
"strokeOpacity",
"strokeWidth",
"tabSize",
"widows",
"zIndex",
"zoom"
const UNITLESS_STYLE_PROPERTIES = /* @__PURE__ */ new Set([
"animationIterationCount",
"aspectRatio",
"borderImageOutset",
"borderImageSlice",
"borderImageWidth",
"columnCount",
"columns",
"fillOpacity",
"flex",
"flexGrow",
"flexShrink",
"fontWeight",
"gridArea",
"gridColumn",
"gridColumnEnd",
"gridColumnStart",
"gridRow",
"gridRowEnd",
"gridRowStart",
"lineClamp",
"lineHeight",
"opacity",
"order",
"orphans",
"scale",
"stopOpacity",
"strokeDasharray",
"strokeDashoffset",
"strokeMiterlimit",
"strokeOpacity",
"strokeWidth",
"tabSize",
"widows",
"zIndex",
"zoom"
]);
var ATTRIBUTE_EXCLUSIONS = /* @__PURE__ */ new Set(["children", "className", "style"]);
var DIRECT_PROPERTY_KEYS = /* @__PURE__ */ new Set(["tabIndex"]);
var FORWARDED_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
"accessKey",
"autoCapitalize",
"contentEditable",
"contextMenu",
"dir",
"draggable",
"enterKeyHint",
"hidden",
"id",
"lang",
"nonce",
"role",
"slot",
"spellCheck",
"tabIndex",
"title",
"translate"
const ATTRIBUTE_EXCLUSIONS = /* @__PURE__ */ new Set([
"children",
"className",
"style"
]);
var SPECIAL_EVENT_NAMES = {
Blur: "focusout",
DoubleClick: "dblclick",
Focus: "focusin",
MouseEnter: "mouseenter",
MouseLeave: "mouseleave"
const DIRECT_PROPERTY_KEYS = /* @__PURE__ */ new Set(["tabIndex"]);
const FORWARDED_ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
"accessKey",
"autoCapitalize",
"contentEditable",
"contextMenu",
"dir",
"draggable",
"enterKeyHint",
"hidden",
"id",
"lang",
"nonce",
"role",
"slot",
"spellCheck",
"tabIndex",
"title",
"translate"
]);
const SPECIAL_EVENT_NAMES = {
Blur: "focusout",
DoubleClick: "dblclick",
Focus: "focusin",
MouseEnter: "mouseenter",
MouseLeave: "mouseleave"
};
function isEventProp(key, value) {
return /^on[A-Z]/.test(key) && typeof value === "function";
return /^on[A-Z]/.test(key) && typeof value === "function";
}
function toAttributeName(key) {
if (key.startsWith("aria-") || key.startsWith("data-")) {
return key;
}
return key;
if (key.startsWith("aria-") || key.startsWith("data-")) return key;
return key;
}
function isForwardedAttributeKey(key) {
return key.startsWith("aria-") || key.startsWith("data-") || FORWARDED_ATTRIBUTE_KEYS.has(key);
return key.startsWith("aria-") || key.startsWith("data-") || FORWARDED_ATTRIBUTE_KEYS.has(key);
}
function toStylePropertyName(key) {
if (key.startsWith("--")) {
return key;
}
return key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
if (key.startsWith("--")) return key;
return key.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`);
}
function toEventConfig(key) {
var _a;
const useCapture = key.endsWith("Capture");
const baseKey = useCapture ? key.slice(0, -7) : key;
const reactEventName = baseKey.slice(2);
const eventName = (_a = SPECIAL_EVENT_NAMES[reactEventName]) != null ? _a : reactEventName.toLowerCase();
return {
eventName,
options: useCapture ? { capture: true } : void 0
};
var _SPECIAL_EVENT_NAMES$;
const useCapture = key.endsWith("Capture");
const reactEventName = (useCapture ? key.slice(0, -7) : key).slice(2);
return {
eventName: (_SPECIAL_EVENT_NAMES$ = SPECIAL_EVENT_NAMES[reactEventName]) !== null && _SPECIAL_EVENT_NAMES$ !== void 0 ? _SPECIAL_EVENT_NAMES$ : reactEventName.toLowerCase(),
options: useCapture ? { capture: true } : void 0
};
}
function createSyntheticEvent(element, nativeEvent) {
let defaultPrevented = nativeEvent.defaultPrevented;
let propagationStopped = false;
const syntheticEvent = Object.create(nativeEvent);
Object.defineProperties(syntheticEvent, {
nativeEvent: { value: nativeEvent },
currentTarget: { value: element },
target: { value: nativeEvent.target },
persist: { value: () => void 0 },
isDefaultPrevented: { value: () => defaultPrevented },
isPropagationStopped: { value: () => propagationStopped },
preventDefault: {
value: () => {
defaultPrevented = true;
nativeEvent.preventDefault();
}
},
stopPropagation: {
value: () => {
propagationStopped = true;
nativeEvent.stopPropagation();
}
}
});
return syntheticEvent;
let defaultPrevented = nativeEvent.defaultPrevented;
let propagationStopped = false;
const syntheticEvent = Object.create(nativeEvent);
Object.defineProperties(syntheticEvent, {
nativeEvent: { value: nativeEvent },
currentTarget: { value: element },
target: { value: nativeEvent.target },
persist: { value: () => void 0 },
isDefaultPrevented: { value: () => defaultPrevented },
isPropagationStopped: { value: () => propagationStopped },
preventDefault: { value: () => {
defaultPrevented = true;
nativeEvent.preventDefault();
} },
stopPropagation: { value: () => {
propagationStopped = true;
nativeEvent.stopPropagation();
} }
});
return syntheticEvent;
}
function isDirectPropertyKey(key) {
return DIRECT_PROPERTY_KEYS.has(key);
return DIRECT_PROPERTY_KEYS.has(key);
}
function setDirectProperty(element, key, value) {
if (key === "tabIndex") {
element.tabIndex = Number(value);
return;
}
;
element[key] = value;
if (key === "tabIndex") {
element.tabIndex = Number(value);
return;
}
element[key] = value;
}
function clearDirectProperty(element, key) {
if (key === "tabIndex") {
element.removeAttribute("tabindex");
return;
}
const propertyValue = element[key];
if (typeof propertyValue === "boolean") {
;
element[key] = false;
return;
}
if (typeof propertyValue === "number") {
;
element[key] = 0;
return;
}
;
element[key] = "";
if (key === "tabIndex") {
element.removeAttribute("tabindex");
return;
}
const propertyValue = element[key];
if (typeof propertyValue === "boolean") {
element[key] = false;
return;
}
if (typeof propertyValue === "number") {
element[key] = 0;
return;
}
element[key] = "";
}
function toStyleValue(styleName, value) {
if (typeof value !== "number" || value === 0 || styleName.startsWith("--") || UNITLESS_STYLE_PROPERTIES.has(styleName)) {
return String(value);
}
return `${value}px`;
if (typeof value !== "number" || value === 0 || styleName.startsWith("--") || UNITLESS_STYLE_PROPERTIES.has(styleName)) return String(value);
return `${value}px`;
}
function removeStyleProperty(element, styleName) {
if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {
return;
}
element.style.removeProperty(toStylePropertyName(styleName));
if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) return;
element.style.removeProperty(toStylePropertyName(styleName));
}
function applyStyleProperty(element, styleName, value) {
if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {
return;
}
element.style.setProperty(toStylePropertyName(styleName), toStyleValue(styleName, value));
if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) return;
element.style.setProperty(toStylePropertyName(styleName), toStyleValue(styleName, value));
}
function syncAttributes(element, prevProps, nextProps) {
const allKeys = /* @__PURE__ */ new Set([...Object.keys(prevProps), ...Object.keys(nextProps)]);
allKeys.forEach((key) => {
if (ATTRIBUTE_EXCLUSIONS.has(key) || !isForwardedAttributeKey(key) || isEventProp(key, prevProps[key]) || isEventProp(key, nextProps[key])) {
return;
}
const prevValue = prevProps[key];
const nextValue = nextProps[key];
if (prevValue === nextValue) {
return;
}
const attributeName = toAttributeName(key);
if (nextValue == null || nextValue === false) {
if (isDirectPropertyKey(key)) {
clearDirectProperty(element, key);
}
element.removeAttribute(attributeName);
return;
}
if (nextValue === true) {
if (isDirectPropertyKey(key)) {
setDirectProperty(element, key, true);
}
element.setAttribute(attributeName, "");
return;
}
if (isDirectPropertyKey(key)) {
setDirectProperty(element, key, nextValue);
return;
}
element.setAttribute(attributeName, String(nextValue));
});
(/* @__PURE__ */ new Set([...Object.keys(prevProps), ...Object.keys(nextProps)])).forEach((key) => {
if (ATTRIBUTE_EXCLUSIONS.has(key) || !isForwardedAttributeKey(key) || isEventProp(key, prevProps[key]) || isEventProp(key, nextProps[key])) return;
const prevValue = prevProps[key];
const nextValue = nextProps[key];
if (prevValue === nextValue) return;
const attributeName = toAttributeName(key);
if (nextValue == null || nextValue === false) {
if (isDirectPropertyKey(key)) clearDirectProperty(element, key);
element.removeAttribute(attributeName);
return;
}
if (nextValue === true) {
if (isDirectPropertyKey(key)) setDirectProperty(element, key, true);
element.setAttribute(attributeName, "");
return;
}
if (isDirectPropertyKey(key)) {
setDirectProperty(element, key, nextValue);
return;
}
element.setAttribute(attributeName, String(nextValue));
});
}
function syncClassName(element, prevClassName, nextClassName) {
if (prevClassName === nextClassName) {
return;
}
if (nextClassName) {
element.className = nextClassName;
return;
}
element.removeAttribute("class");
if (prevClassName === nextClassName) return;
if (nextClassName) {
element.className = nextClassName;
return;
}
element.removeAttribute("class");
}
function syncStyles(element, prevStyle, nextStyle) {
const previousStyle = prevStyle != null ? prevStyle : {};
const currentStyle = nextStyle != null ? nextStyle : {};
const allStyleNames = /* @__PURE__ */ new Set([...Object.keys(previousStyle), ...Object.keys(currentStyle)]);
allStyleNames.forEach((styleName) => {
const prevValue = previousStyle[styleName];
const nextValue = currentStyle[styleName];
if (prevValue === nextValue) {
return;
}
if (nextValue == null) {
removeStyleProperty(element, styleName);
return;
}
applyStyleProperty(element, styleName, nextValue);
});
const previousStyle = prevStyle !== null && prevStyle !== void 0 ? prevStyle : {};
const currentStyle = nextStyle !== null && nextStyle !== void 0 ? nextStyle : {};
(/* @__PURE__ */ new Set([...Object.keys(previousStyle), ...Object.keys(currentStyle)])).forEach((styleName) => {
const prevValue = previousStyle[styleName];
const nextValue = currentStyle[styleName];
if (prevValue === nextValue) return;
if (nextValue == null) {
removeStyleProperty(element, styleName);
return;
}
applyStyleProperty(element, styleName, nextValue);
});
}
function syncEventListeners(element, prevListeners, nextProps) {
prevListeners.forEach(({ eventName, listener, options }) => {
element.removeEventListener(eventName, listener, options);
});
const nextListeners = [];
Object.entries(nextProps).forEach(([key, value]) => {
if (!isEventProp(key, value)) {
return;
}
const { eventName, options } = toEventConfig(key);
const listener = (event) => {
value(createSyntheticEvent(element, event));
};
element.addEventListener(eventName, listener, options);
nextListeners.push({ eventName, listener, options });
});
return nextListeners;
prevListeners.forEach(({ eventName, listener, options }) => {
element.removeEventListener(eventName, listener, options);
});
const nextListeners = [];
Object.entries(nextProps).forEach(([key, value]) => {
if (!isEventProp(key, value)) return;
const { eventName, options } = toEventConfig(key);
const listener = (event) => {
value(createSyntheticEvent(element, event));
};
element.addEventListener(eventName, listener, options);
nextListeners.push({
eventName,
listener,
options
});
});
return nextListeners;
}
function useMenuElementProps(element, props) {
const previousPropsRef = useRef({});
const listenersRef = useRef([]);
useIsomorphicLayoutEffect(() => {
const previousProps = previousPropsRef.current;
syncClassName(element, previousProps.className, props.className);
syncStyles(element, previousProps.style, props.style);
syncAttributes(element, previousProps, props);
listenersRef.current = syncEventListeners(element, listenersRef.current, props);
previousPropsRef.current = props;
return () => {
listenersRef.current.forEach(({ eventName, listener, options }) => {
element.removeEventListener(eventName, listener, options);
});
listenersRef.current = [];
};
}, [element, props]);
const previousPropsRef = useRef({});
const listenersRef = useRef([]);
useIsomorphicLayoutEffect(() => {
const previousProps = previousPropsRef.current;
syncClassName(element, previousProps.className, props.className);
syncStyles(element, previousProps.style, props.style);
syncAttributes(element, previousProps, props);
listenersRef.current = syncEventListeners(element, listenersRef.current, props);
previousPropsRef.current = props;
return () => {
listenersRef.current.forEach(({ eventName, listener, options }) => {
element.removeEventListener(eventName, listener, options);
});
listenersRef.current = [];
};
}, [element, props]);
}
//#endregion
//#region src/menus/BubbleMenu.tsx
const BubbleMenu = React.forwardRef(({ pluginKey, editor, updateDelay, resizeDelay, appendTo, shouldShow = null, getReferencedVirtualElement, options, children, ...restProps }, ref) => {
const menuEl = useRef(document.createElement("div"));
const resolvedPluginKey = useRef(getAutoPluginKey(pluginKey, "bubbleMenu")).current;
useMenuElementProps(menuEl.current, restProps);
if (typeof ref === "function") ref(menuEl.current);
else if (ref) ref.current = menuEl.current;
const { editor: currentEditor } = useCurrentEditor();
/**
* The editor instance where the bubble menu plugin will be registered.
*/
const pluginEditor = editor || currentEditor;
const bubbleMenuPluginProps = {
updateDelay,
resizeDelay,
appendTo,
pluginKey: resolvedPluginKey,
shouldShow,
getReferencedVirtualElement,
options
};
/**
* The props for the bubble menu plugin. They are accessed inside a ref to
* avoid running the useEffect hook and re-registering the plugin when the
* props change.
*/
const bubbleMenuPluginPropsRef = useRef(bubbleMenuPluginProps);
bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps;
/**
* Track whether the plugin has been initialized, so we only send updates
* after the initial registration.
*/
const [pluginInitialized, setPluginInitialized] = useState(false);
/**
* Track whether we need to skip the first options update dispatch.
* This prevents unnecessary updates right after plugin initialization.
*/
const skipFirstUpdateRef = useRef(true);
useEffect(() => {
if (pluginEditor === null || pluginEditor === void 0 ? void 0 : pluginEditor.isDestroyed) return;
if (!pluginEditor) {
console.warn("BubbleMenu component is not rendered inside of an editor component or does not have editor prop.");
return;
}
const bubbleMenuElement = menuEl.current;
bubbleMenuElement.style.visibility = "hidden";
bubbleMenuElement.style.position = "absolute";
const plugin = BubbleMenuPlugin({
...bubbleMenuPluginPropsRef.current,
editor: pluginEditor,
element: bubbleMenuElement
});
pluginEditor.registerPlugin(plugin);
const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey;
skipFirstUpdateRef.current = true;
setPluginInitialized(true);
return () => {
setPluginInitialized(false);
pluginEditor.unregisterPlugin(createdPluginKey);
window.requestAnimationFrame(() => {
if (bubbleMenuElement.parentNode) bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
});
};
}, [pluginEditor]);
/**
* Update the plugin options when props change after the plugin has been initialized.
* This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.
*/
useEffect(() => {
if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) return;
if (skipFirstUpdateRef.current) {
skipFirstUpdateRef.current = false;
return;
}
pluginEditor.view.dispatch(pluginEditor.state.tr.setMeta(resolvedPluginKey, {
type: "updateOptions",
options: bubbleMenuPluginPropsRef.current
}));
}, [
pluginInitialized,
pluginEditor,
updateDelay,
resizeDelay,
shouldShow,
options,
appendTo,
getReferencedVirtualElement,
resolvedPluginKey
]);
return createPortal(children, menuEl.current);
});
//#endregion
//#region src/menus/FloatingMenu.tsx
const FloatingMenu = React.forwardRef(({ pluginKey, editor, updateDelay, resizeDelay, appendTo, shouldShow = null, options, children, ...restProps }, ref) => {
const menuEl = useRef(document.createElement("div"));
const resolvedPluginKey = useRef(getAutoPluginKey(pluginKey, "floatingMenu")).current;
useMenuElementProps(menuEl.current, restProps);
if (typeof ref === "function") ref(menuEl.current);
else if (ref) ref.current = menuEl.current;
const { editor: currentEditor } = useCurrentEditor();
/**
* The editor instance where the floating menu plugin will be registered.
*/
const pluginEditor = editor || currentEditor;
const floatingMenuPluginProps = {
updateDelay,
resizeDelay,
appendTo,
pluginKey: resolvedPluginKey,
shouldShow,
options
};
/**
* The props for the floating menu plugin. They are accessed inside a ref to
* avoid running the useEffect hook and re-registering the plugin when the
* props change.
*/
const floatingMenuPluginPropsRef = useRef(floatingMenuPluginProps);
floatingMenuPluginPropsRef.current = floatingMenuPluginProps;
/**
* Track whether the plugin has been initialized, so we only send updates
* after the initial registration.
*/
const [pluginInitialized, setPluginInitialized] = useState(false);
/**
* Track whether we need to skip the first options update dispatch.
* This prevents unnecessary updates right after plugin initialization.
*/
const skipFirstUpdateRef = useRef(true);
useEffect(() => {
if (pluginEditor === null || pluginEditor === void 0 ? void 0 : pluginEditor.isDestroyed) return;
if (!pluginEditor) {
console.warn("FloatingMenu component is not rendered inside of an editor component or does not have editor prop.");
return;
}
const floatingMenuElement = menuEl.current;
floatingMenuElement.style.visibility = "hidden";
floatingMenuElement.style.position = "absolute";
const plugin = FloatingMenuPlugin({
...floatingMenuPluginPropsRef.current,
editor: pluginEditor,
element: floatingMenuElement
});
pluginEditor.registerPlugin(plugin);
const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey;
skipFirstUpdateRef.current = true;
setPluginInitialized(true);
return () => {
setPluginInitialized(false);
pluginEditor.unregisterPlugin(createdPluginKey);
window.requestAnimationFrame(() => {
if (floatingMenuElement.parentNode) floatingMenuElement.parentNode.removeChild(floatingMenuElement);
});
};
}, [pluginEditor]);
/**
* Update the plugin options when props change after the plugin has been initialized.
* This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.
*/
useEffect(() => {
if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) return;
if (skipFirstUpdateRef.current) {
skipFirstUpdateRef.current = false;
return;
}
pluginEditor.view.dispatch(pluginEditor.state.tr.setMeta(resolvedPluginKey, {
type: "updateOptions",
options: floatingMenuPluginPropsRef.current
}));
}, [
pluginInitialized,
pluginEditor,
updateDelay,
resizeDelay,
shouldShow,
options,
appendTo,
resolvedPluginKey
]);
return createPortal(children, menuEl.current);
});
//#endregion
export { BubbleMenu, FloatingMenu };
// src/menus/BubbleMenu.tsx
var BubbleMenu = React.forwardRef(
({
pluginKey,
editor,
updateDelay,
resizeDelay,
appendTo,
shouldShow = null,
getReferencedVirtualElement,
options,
children,
...restProps
}, ref) => {
const menuEl = useRef2(document.createElement("div"));
const resolvedPluginKey = useRef2(
getAutoPluginKey(pluginKey, "bubbleMenu")
).current;
useMenuElementProps(menuEl.current, restProps);
if (typeof ref === "function") {
ref(menuEl.current);
} else if (ref) {
ref.current = menuEl.current;
}
const { editor: currentEditor } = useCurrentEditor();
const pluginEditor = editor || currentEditor;
const bubbleMenuPluginProps = {
updateDelay,
resizeDelay,
appendTo,
pluginKey: resolvedPluginKey,
shouldShow,
getReferencedVirtualElement,
options
};
const bubbleMenuPluginPropsRef = useRef2(bubbleMenuPluginProps);
bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps;
const [pluginInitialized, setPluginInitialized] = useState(false);
const skipFirstUpdateRef = useRef2(true);
useEffect2(() => {
if (pluginEditor == null ? void 0 : pluginEditor.isDestroyed) {
return;
}
if (!pluginEditor) {
console.warn(
"BubbleMenu component is not rendered inside of an editor component or does not have editor prop."
);
return;
}
const bubbleMenuElement = menuEl.current;
bubbleMenuElement.style.visibility = "hidden";
bubbleMenuElement.style.position = "absolute";
const plugin = BubbleMenuPlugin({
...bubbleMenuPluginPropsRef.current,
editor: pluginEditor,
element: bubbleMenuElement
});
pluginEditor.registerPlugin(plugin);
const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey;
skipFirstUpdateRef.current = true;
setPluginInitialized(true);
return () => {
setPluginInitialized(false);
pluginEditor.unregisterPlugin(createdPluginKey);
window.requestAnimationFrame(() => {
if (bubbleMenuElement.parentNode) {
bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
}
});
};
}, [pluginEditor]);
useEffect2(() => {
if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {
return;
}
if (skipFirstUpdateRef.current) {
skipFirstUpdateRef.current = false;
return;
}
pluginEditor.view.dispatch(
pluginEditor.state.tr.setMeta(resolvedPluginKey, {
type: "updateOptions",
options: bubbleMenuPluginPropsRef.current
})
);
}, [
pluginInitialized,
pluginEditor,
updateDelay,
resizeDelay,
shouldShow,
options,
appendTo,
getReferencedVirtualElement,
resolvedPluginKey
]);
return createPortal(children, menuEl.current);
}
);
// src/menus/FloatingMenu.tsx
import { FloatingMenuPlugin } from "@tiptap/extension-floating-menu";
import { useCurrentEditor as useCurrentEditor2 } from "@tiptap/react";
import React2, { useEffect as useEffect3, useRef as useRef3, useState as useState2 } from "react";
import { createPortal as createPortal2 } from "react-dom";
var FloatingMenu = React2.forwardRef(
({
pluginKey,
editor,
updateDelay,
resizeDelay,
appendTo,
shouldShow = null,
options,
children,
...restProps
}, ref) => {
const menuEl = useRef3(document.createElement("div"));
const resolvedPluginKey = useRef3(
getAutoPluginKey(pluginKey, "floatingMenu")
).current;
useMenuElementProps(menuEl.current, restProps);
if (typeof ref === "function") {
ref(menuEl.current);
} else if (ref) {
ref.current = menuEl.current;
}
const { editor: currentEditor } = useCurrentEditor2();
const pluginEditor = editor || currentEditor;
const floatingMenuPluginProps = {
updateDelay,
resizeDelay,
appendTo,
pluginKey: resolvedPluginKey,
shouldShow,
options
};
const floatingMenuPluginPropsRef = useRef3(floatingMenuPluginProps);
floatingMenuPluginPropsRef.current = floatingMenuPluginProps;
const [pluginInitialized, setPluginInitialized] = useState2(false);
const skipFirstUpdateRef = useRef3(true);
useEffect3(() => {
if (pluginEditor == null ? void 0 : pluginEditor.isDestroyed) {
return;
}
if (!pluginEditor) {
console.warn(
"FloatingMenu component is not rendered inside of an editor component or does not have editor prop."
);
return;
}
const floatingMenuElement = menuEl.current;
floatingMenuElement.style.visibility = "hidden";
floatingMenuElement.style.position = "absolute";
const plugin = FloatingMenuPlugin({
...floatingMenuPluginPropsRef.current,
editor: pluginEditor,
element: floatingMenuElement
});
pluginEditor.registerPlugin(plugin);
const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey;
skipFirstUpdateRef.current = true;
setPluginInitialized(true);
return () => {
setPluginInitialized(false);
pluginEditor.unregisterPlugin(createdPluginKey);
window.requestAnimationFrame(() => {
if (floatingMenuElement.parentNode) {
floatingMenuElement.parentNode.removeChild(floatingMenuElement);
}
});
};
}, [pluginEditor]);
useEffect3(() => {
if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {
return;
}
if (skipFirstUpdateRef.current) {
skipFirstUpdateRef.current = false;
return;
}
pluginEditor.view.dispatch(
pluginEditor.state.tr.setMeta(resolvedPluginKey, {
type: "updateOptions",
options: floatingMenuPluginPropsRef.current
})
);
}, [
pluginInitialized,
pluginEditor,
updateDelay,
resizeDelay,
shouldShow,
options,
appendTo,
resolvedPluginKey
]);
return createPortal2(children, menuEl.current);
}
);
export {
BubbleMenu,
FloatingMenu
};
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../src/menus/BubbleMenu.tsx","../../src/menus/getAutoPluginKey.ts","../../src/menus/useMenuElementProps.ts","../../src/menus/FloatingMenu.tsx"],"sourcesContent":["import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'\nimport type { PluginKey } from '@tiptap/pm/state'\nimport { useCurrentEditor } from '@tiptap/react'\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getAutoPluginKey } from './getAutoPluginKey.js'\nimport { useMenuElementProps } from './useMenuElementProps.js'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type BubbleMenuProps = Optional<\n Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>,\n 'editor'\n> &\n React.HTMLAttributes<HTMLDivElement>\n\nexport const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(\n (\n {\n pluginKey,\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n getReferencedVirtualElement,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n const resolvedPluginKey = useRef<PluginKey | string>(\n getAutoPluginKey(pluginKey, 'bubbleMenu'),\n ).current\n\n useMenuElementProps(menuEl.current, restProps)\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n /**\n * The editor instance where the bubble menu plugin will be registered.\n */\n const pluginEditor = editor || currentEditor\n\n // Creating a useMemo would be more computationally expensive than just\n // re-creating this object on every render.\n const bubbleMenuPluginProps: Omit<BubbleMenuPluginProps, 'editor' | 'element'> = {\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey: resolvedPluginKey,\n shouldShow,\n getReferencedVirtualElement,\n options,\n }\n /**\n * The props for the bubble menu plugin. They are accessed inside a ref to\n * avoid running the useEffect hook and re-registering the plugin when the\n * props change.\n */\n const bubbleMenuPluginPropsRef = useRef(bubbleMenuPluginProps)\n bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps\n\n /**\n * Track whether the plugin has been initialized, so we only send updates\n * after the initial registration.\n */\n const [pluginInitialized, setPluginInitialized] = useState(false)\n\n /**\n * Track whether we need to skip the first options update dispatch.\n * This prevents unnecessary updates right after plugin initialization.\n */\n const skipFirstUpdateRef = useRef(true)\n\n useEffect(() => {\n if (pluginEditor?.isDestroyed) {\n return\n }\n\n if (!pluginEditor) {\n console.warn(\n 'BubbleMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const bubbleMenuElement = menuEl.current\n bubbleMenuElement.style.visibility = 'hidden'\n bubbleMenuElement.style.position = 'absolute'\n\n const plugin = BubbleMenuPlugin({\n ...bubbleMenuPluginPropsRef.current,\n editor: pluginEditor,\n element: bubbleMenuElement,\n })\n\n pluginEditor.registerPlugin(plugin)\n\n const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey\n\n skipFirstUpdateRef.current = true\n setPluginInitialized(true)\n\n return () => {\n setPluginInitialized(false)\n pluginEditor.unregisterPlugin(createdPluginKey)\n window.requestAnimationFrame(() => {\n if (bubbleMenuElement.parentNode) {\n bubbleMenuElement.parentNode.removeChild(bubbleMenuElement)\n }\n })\n }\n }, [pluginEditor])\n\n /**\n * Update the plugin options when props change after the plugin has been initialized.\n * This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.\n */\n useEffect(() => {\n if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {\n return\n }\n\n // Skip the first update right after initialization since the plugin was just created with these options\n if (skipFirstUpdateRef.current) {\n skipFirstUpdateRef.current = false\n return\n }\n\n pluginEditor.view.dispatch(\n pluginEditor.state.tr.setMeta(resolvedPluginKey, {\n type: 'updateOptions',\n options: bubbleMenuPluginPropsRef.current,\n }),\n )\n }, [\n pluginInitialized,\n pluginEditor,\n updateDelay,\n resizeDelay,\n shouldShow,\n options,\n appendTo,\n getReferencedVirtualElement,\n resolvedPluginKey,\n ])\n\n return createPortal(children, menuEl.current)\n },\n)\n","import { PluginKey } from '@tiptap/pm/state'\n\nexport function getAutoPluginKey(pluginKey: PluginKey | string | undefined, defaultName: string) {\n return pluginKey ?? new PluginKey(defaultName)\n}\n","import type { CSSProperties, HTMLAttributes } from 'react'\nimport { useEffect, useLayoutEffect, useRef } from 'react'\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\ntype MenuElementProps = HTMLAttributes<HTMLDivElement>\ntype MenuSyntheticEvent = Event & {\n nativeEvent: Event\n currentTarget: HTMLDivElement\n target: EventTarget | null\n persist: () => void\n isDefaultPrevented: () => boolean\n isPropagationStopped: () => boolean\n}\ntype MenuEventListener = (event: MenuSyntheticEvent) => void\ntype MenuNativeListener = (event: Event) => void\ntype MenuEventListenerOptions = {\n capture?: boolean\n}\n\ntype EventListenerEntry = {\n eventName: string\n listener: MenuNativeListener\n options?: MenuEventListenerOptions\n}\n\nconst PLUGIN_MANAGED_STYLE_PROPERTIES = new Set([\n 'left',\n 'opacity',\n 'position',\n 'top',\n 'visibility',\n 'width',\n])\nconst UNITLESS_STYLE_PROPERTIES = new Set([\n 'animationIterationCount',\n 'aspectRatio',\n 'borderImageOutset',\n 'borderImageSlice',\n 'borderImageWidth',\n 'columnCount',\n 'columns',\n 'fillOpacity',\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'fontWeight',\n 'gridArea',\n 'gridColumn',\n 'gridColumnEnd',\n 'gridColumnStart',\n 'gridRow',\n 'gridRowEnd',\n 'gridRowStart',\n 'lineClamp',\n 'lineHeight',\n 'opacity',\n 'order',\n 'orphans',\n 'scale',\n 'stopOpacity',\n 'strokeDasharray',\n 'strokeDashoffset',\n 'strokeMiterlimit',\n 'strokeOpacity',\n 'strokeWidth',\n 'tabSize',\n 'widows',\n 'zIndex',\n 'zoom',\n])\nconst ATTRIBUTE_EXCLUSIONS = new Set(['children', 'className', 'style'])\nconst DIRECT_PROPERTY_KEYS = new Set(['tabIndex'])\nconst FORWARDED_ATTRIBUTE_KEYS = new Set([\n 'accessKey',\n 'autoCapitalize',\n 'contentEditable',\n 'contextMenu',\n 'dir',\n 'draggable',\n 'enterKeyHint',\n 'hidden',\n 'id',\n 'lang',\n 'nonce',\n 'role',\n 'slot',\n 'spellCheck',\n 'tabIndex',\n 'title',\n 'translate',\n])\nconst SPECIAL_EVENT_NAMES: Record<string, string> = {\n Blur: 'focusout',\n DoubleClick: 'dblclick',\n Focus: 'focusin',\n MouseEnter: 'mouseenter',\n MouseLeave: 'mouseleave',\n}\n\nfunction isEventProp(key: string, value: unknown): value is MenuEventListener {\n return /^on[A-Z]/.test(key) && typeof value === 'function'\n}\n\nfunction toAttributeName(key: string) {\n if (key.startsWith('aria-') || key.startsWith('data-')) {\n return key\n }\n\n return key\n}\n\nfunction isForwardedAttributeKey(key: string) {\n return key.startsWith('aria-') || key.startsWith('data-') || FORWARDED_ATTRIBUTE_KEYS.has(key)\n}\n\nfunction toStylePropertyName(key: string) {\n if (key.startsWith('--')) {\n return key\n }\n\n return key.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)\n}\n\nfunction toEventConfig(key: string) {\n const useCapture = key.endsWith('Capture')\n const baseKey = useCapture ? key.slice(0, -7) : key\n const reactEventName = baseKey.slice(2)\n const eventName = SPECIAL_EVENT_NAMES[reactEventName] ?? reactEventName.toLowerCase()\n\n return {\n eventName,\n options: useCapture ? { capture: true } : undefined,\n }\n}\n\nfunction createSyntheticEvent(element: HTMLDivElement, nativeEvent: Event): MenuSyntheticEvent {\n let defaultPrevented = nativeEvent.defaultPrevented\n let propagationStopped = false\n const syntheticEvent = Object.create(nativeEvent)\n\n Object.defineProperties(syntheticEvent, {\n nativeEvent: { value: nativeEvent },\n currentTarget: { value: element },\n target: { value: nativeEvent.target },\n persist: { value: () => undefined },\n isDefaultPrevented: { value: () => defaultPrevented },\n isPropagationStopped: { value: () => propagationStopped },\n preventDefault: {\n value: () => {\n defaultPrevented = true\n nativeEvent.preventDefault()\n },\n },\n stopPropagation: {\n value: () => {\n propagationStopped = true\n nativeEvent.stopPropagation()\n },\n },\n })\n\n return syntheticEvent as MenuSyntheticEvent\n}\n\nfunction isDirectPropertyKey(key: string) {\n return DIRECT_PROPERTY_KEYS.has(key)\n}\n\nfunction setDirectProperty(element: HTMLDivElement, key: string, value: unknown) {\n if (key === 'tabIndex') {\n element.tabIndex = Number(value)\n return\n }\n\n ;(element as unknown as Record<string, unknown>)[key] = value\n}\n\nfunction clearDirectProperty(element: HTMLDivElement, key: string) {\n if (key === 'tabIndex') {\n element.removeAttribute('tabindex')\n return\n }\n\n const propertyValue = (element as unknown as Record<string, unknown>)[key]\n\n if (typeof propertyValue === 'boolean') {\n ;(element as unknown as Record<string, unknown>)[key] = false\n return\n }\n\n if (typeof propertyValue === 'number') {\n ;(element as unknown as Record<string, unknown>)[key] = 0\n return\n }\n\n ;(element as unknown as Record<string, unknown>)[key] = ''\n}\n\nfunction toStyleValue(styleName: string, value: string | number) {\n if (\n typeof value !== 'number' ||\n value === 0 ||\n styleName.startsWith('--') ||\n UNITLESS_STYLE_PROPERTIES.has(styleName)\n ) {\n return String(value)\n }\n\n return `${value}px`\n}\n\nfunction removeStyleProperty(element: HTMLDivElement, styleName: string) {\n if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {\n return\n }\n\n element.style.removeProperty(toStylePropertyName(styleName))\n}\n\nfunction applyStyleProperty(element: HTMLDivElement, styleName: string, value: string | number) {\n if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {\n return\n }\n\n element.style.setProperty(toStylePropertyName(styleName), toStyleValue(styleName, value))\n}\n\nfunction syncAttributes(\n element: HTMLDivElement,\n prevProps: MenuElementProps,\n nextProps: MenuElementProps,\n) {\n const allKeys = new Set([...Object.keys(prevProps), ...Object.keys(nextProps)])\n\n allKeys.forEach(key => {\n if (\n ATTRIBUTE_EXCLUSIONS.has(key) ||\n !isForwardedAttributeKey(key) ||\n isEventProp(key, prevProps[key as keyof MenuElementProps]) ||\n isEventProp(key, nextProps[key as keyof MenuElementProps])\n ) {\n return\n }\n\n const prevValue = prevProps[key as keyof MenuElementProps]\n const nextValue = nextProps[key as keyof MenuElementProps]\n\n if (prevValue === nextValue) {\n return\n }\n\n const attributeName = toAttributeName(key)\n\n if (nextValue == null || nextValue === false) {\n if (isDirectPropertyKey(key)) {\n clearDirectProperty(element, key)\n }\n\n element.removeAttribute(attributeName)\n return\n }\n\n if (nextValue === true) {\n if (isDirectPropertyKey(key)) {\n setDirectProperty(element, key, true)\n }\n\n element.setAttribute(attributeName, '')\n return\n }\n\n if (isDirectPropertyKey(key)) {\n setDirectProperty(element, key, nextValue)\n return\n }\n\n element.setAttribute(attributeName, String(nextValue))\n })\n}\n\nfunction syncClassName(element: HTMLDivElement, prevClassName?: string, nextClassName?: string) {\n if (prevClassName === nextClassName) {\n return\n }\n\n if (nextClassName) {\n element.className = nextClassName\n return\n }\n\n element.removeAttribute('class')\n}\n\nfunction syncStyles(\n element: HTMLDivElement,\n prevStyle: CSSProperties | undefined,\n nextStyle: CSSProperties | undefined,\n) {\n const previousStyle = prevStyle ?? {}\n const currentStyle = nextStyle ?? {}\n const allStyleNames = new Set([...Object.keys(previousStyle), ...Object.keys(currentStyle)])\n\n allStyleNames.forEach(styleName => {\n const prevValue = previousStyle[styleName as keyof CSSProperties]\n const nextValue = currentStyle[styleName as keyof CSSProperties]\n\n if (prevValue === nextValue) {\n return\n }\n\n if (nextValue == null) {\n removeStyleProperty(element, styleName)\n return\n }\n\n applyStyleProperty(element, styleName, nextValue as string | number)\n })\n}\n\nfunction syncEventListeners(\n element: HTMLDivElement,\n prevListeners: EventListenerEntry[],\n nextProps: MenuElementProps,\n) {\n prevListeners.forEach(({ eventName, listener, options }) => {\n element.removeEventListener(eventName, listener, options)\n })\n\n const nextListeners: EventListenerEntry[] = []\n\n Object.entries(nextProps).forEach(([key, value]) => {\n if (!isEventProp(key, value)) {\n return\n }\n\n const { eventName, options } = toEventConfig(key)\n const listener: MenuNativeListener = event => {\n value(createSyntheticEvent(element, event))\n }\n\n element.addEventListener(eventName, listener, options)\n nextListeners.push({ eventName, listener, options })\n })\n\n return nextListeners\n}\n\nexport function useMenuElementProps(element: HTMLDivElement, props: MenuElementProps) {\n const previousPropsRef = useRef<MenuElementProps>({})\n const listenersRef = useRef<EventListenerEntry[]>([])\n\n useIsomorphicLayoutEffect(() => {\n const previousProps = previousPropsRef.current\n\n syncClassName(element, previousProps.className, props.className)\n syncStyles(element, previousProps.style, props.style)\n syncAttributes(element, previousProps, props)\n listenersRef.current = syncEventListeners(element, listenersRef.current, props)\n previousPropsRef.current = props\n\n return () => {\n listenersRef.current.forEach(({ eventName, listener, options }) => {\n element.removeEventListener(eventName, listener, options)\n })\n listenersRef.current = []\n }\n }, [element, props])\n}\n","import type { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'\nimport { FloatingMenuPlugin } from '@tiptap/extension-floating-menu'\nimport type { PluginKey } from '@tiptap/pm/state'\nimport { useCurrentEditor } from '@tiptap/react'\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getAutoPluginKey } from './getAutoPluginKey.js'\nimport { useMenuElementProps } from './useMenuElementProps.js'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type FloatingMenuProps = Omit<\n Optional<FloatingMenuPluginProps, 'pluginKey'>,\n 'element' | 'editor'\n> & {\n editor: FloatingMenuPluginProps['editor'] | null\n options?: FloatingMenuPluginProps['options']\n} & React.HTMLAttributes<HTMLDivElement>\n\nexport const FloatingMenu = React.forwardRef<HTMLDivElement, FloatingMenuProps>(\n (\n {\n pluginKey,\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n const resolvedPluginKey = useRef<PluginKey | string>(\n getAutoPluginKey(pluginKey, 'floatingMenu'),\n ).current\n\n useMenuElementProps(menuEl.current, restProps)\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n /**\n * The editor instance where the floating menu plugin will be registered.\n */\n const pluginEditor = editor || currentEditor\n\n // Creating a useMemo would be more computationally expensive than just\n // re-creating this object on every render.\n const floatingMenuPluginProps: Omit<FloatingMenuPluginProps, 'editor' | 'element'> = {\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey: resolvedPluginKey,\n shouldShow,\n options,\n }\n\n /**\n * The props for the floating menu plugin. They are accessed inside a ref to\n * avoid running the useEffect hook and re-registering the plugin when the\n * props change.\n */\n const floatingMenuPluginPropsRef = useRef(floatingMenuPluginProps)\n floatingMenuPluginPropsRef.current = floatingMenuPluginProps\n\n /**\n * Track whether the plugin has been initialized, so we only send updates\n * after the initial registration.\n */\n const [pluginInitialized, setPluginInitialized] = useState(false)\n\n /**\n * Track whether we need to skip the first options update dispatch.\n * This prevents unnecessary updates right after plugin initialization.\n */\n const skipFirstUpdateRef = useRef(true)\n\n useEffect(() => {\n if (pluginEditor?.isDestroyed) {\n return\n }\n\n if (!pluginEditor) {\n console.warn(\n 'FloatingMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const floatingMenuElement = menuEl.current\n floatingMenuElement.style.visibility = 'hidden'\n floatingMenuElement.style.position = 'absolute'\n\n const plugin = FloatingMenuPlugin({\n ...floatingMenuPluginPropsRef.current,\n editor: pluginEditor,\n element: floatingMenuElement,\n })\n\n pluginEditor.registerPlugin(plugin)\n\n const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey\n\n skipFirstUpdateRef.current = true\n setPluginInitialized(true)\n\n return () => {\n setPluginInitialized(false)\n pluginEditor.unregisterPlugin(createdPluginKey)\n window.requestAnimationFrame(() => {\n if (floatingMenuElement.parentNode) {\n floatingMenuElement.parentNode.removeChild(floatingMenuElement)\n }\n })\n }\n }, [pluginEditor])\n\n /**\n * Update the plugin options when props change after the plugin has been initialized.\n * This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.\n */\n useEffect(() => {\n if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {\n return\n }\n\n // Skip the first update right after initialization since the plugin was just created with these options\n if (skipFirstUpdateRef.current) {\n skipFirstUpdateRef.current = false\n return\n }\n\n pluginEditor.view.dispatch(\n pluginEditor.state.tr.setMeta(resolvedPluginKey, {\n type: 'updateOptions',\n options: floatingMenuPluginPropsRef.current,\n }),\n )\n }, [\n pluginInitialized,\n pluginEditor,\n updateDelay,\n resizeDelay,\n shouldShow,\n options,\n appendTo,\n resolvedPluginKey,\n ])\n\n return createPortal(children, menuEl.current)\n },\n)\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect","useCurrentEditor","React","useEffect","useRef","useState","createPortal","React","useRef","useCurrentEditor","useState","useEffect","createPortal"]}
{"version":3,"file":"index.js","names":[],"sources":["../../src/menus/getAutoPluginKey.ts","../../src/menus/useMenuElementProps.ts","../../src/menus/BubbleMenu.tsx","../../src/menus/FloatingMenu.tsx"],"sourcesContent":["import { PluginKey } from '@tiptap/pm/state'\n\nexport function getAutoPluginKey(pluginKey: PluginKey | string | undefined, defaultName: string) {\n return pluginKey ?? new PluginKey(defaultName)\n}\n","import type { CSSProperties, HTMLAttributes } from 'react'\nimport { useEffect, useLayoutEffect, useRef } from 'react'\n\nconst useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect\n\ntype MenuElementProps = HTMLAttributes<HTMLDivElement>\ntype MenuSyntheticEvent = Event & {\n nativeEvent: Event\n currentTarget: HTMLDivElement\n target: EventTarget | null\n persist: () => void\n isDefaultPrevented: () => boolean\n isPropagationStopped: () => boolean\n}\ntype MenuEventListener = (event: MenuSyntheticEvent) => void\ntype MenuNativeListener = (event: Event) => void\ntype MenuEventListenerOptions = {\n capture?: boolean\n}\n\ntype EventListenerEntry = {\n eventName: string\n listener: MenuNativeListener\n options?: MenuEventListenerOptions\n}\n\nconst PLUGIN_MANAGED_STYLE_PROPERTIES = new Set([\n 'left',\n 'opacity',\n 'position',\n 'top',\n 'visibility',\n 'width',\n])\nconst UNITLESS_STYLE_PROPERTIES = new Set([\n 'animationIterationCount',\n 'aspectRatio',\n 'borderImageOutset',\n 'borderImageSlice',\n 'borderImageWidth',\n 'columnCount',\n 'columns',\n 'fillOpacity',\n 'flex',\n 'flexGrow',\n 'flexShrink',\n 'fontWeight',\n 'gridArea',\n 'gridColumn',\n 'gridColumnEnd',\n 'gridColumnStart',\n 'gridRow',\n 'gridRowEnd',\n 'gridRowStart',\n 'lineClamp',\n 'lineHeight',\n 'opacity',\n 'order',\n 'orphans',\n 'scale',\n 'stopOpacity',\n 'strokeDasharray',\n 'strokeDashoffset',\n 'strokeMiterlimit',\n 'strokeOpacity',\n 'strokeWidth',\n 'tabSize',\n 'widows',\n 'zIndex',\n 'zoom',\n])\nconst ATTRIBUTE_EXCLUSIONS = new Set(['children', 'className', 'style'])\nconst DIRECT_PROPERTY_KEYS = new Set(['tabIndex'])\nconst FORWARDED_ATTRIBUTE_KEYS = new Set([\n 'accessKey',\n 'autoCapitalize',\n 'contentEditable',\n 'contextMenu',\n 'dir',\n 'draggable',\n 'enterKeyHint',\n 'hidden',\n 'id',\n 'lang',\n 'nonce',\n 'role',\n 'slot',\n 'spellCheck',\n 'tabIndex',\n 'title',\n 'translate',\n])\nconst SPECIAL_EVENT_NAMES: Record<string, string> = {\n Blur: 'focusout',\n DoubleClick: 'dblclick',\n Focus: 'focusin',\n MouseEnter: 'mouseenter',\n MouseLeave: 'mouseleave',\n}\n\nfunction isEventProp(key: string, value: unknown): value is MenuEventListener {\n return /^on[A-Z]/.test(key) && typeof value === 'function'\n}\n\nfunction toAttributeName(key: string) {\n if (key.startsWith('aria-') || key.startsWith('data-')) {\n return key\n }\n\n return key\n}\n\nfunction isForwardedAttributeKey(key: string) {\n return key.startsWith('aria-') || key.startsWith('data-') || FORWARDED_ATTRIBUTE_KEYS.has(key)\n}\n\nfunction toStylePropertyName(key: string) {\n if (key.startsWith('--')) {\n return key\n }\n\n return key.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`)\n}\n\nfunction toEventConfig(key: string) {\n const useCapture = key.endsWith('Capture')\n const baseKey = useCapture ? key.slice(0, -7) : key\n const reactEventName = baseKey.slice(2)\n const eventName = SPECIAL_EVENT_NAMES[reactEventName] ?? reactEventName.toLowerCase()\n\n return {\n eventName,\n options: useCapture ? { capture: true } : undefined,\n }\n}\n\nfunction createSyntheticEvent(element: HTMLDivElement, nativeEvent: Event): MenuSyntheticEvent {\n let defaultPrevented = nativeEvent.defaultPrevented\n let propagationStopped = false\n const syntheticEvent = Object.create(nativeEvent)\n\n Object.defineProperties(syntheticEvent, {\n nativeEvent: { value: nativeEvent },\n currentTarget: { value: element },\n target: { value: nativeEvent.target },\n persist: { value: () => undefined },\n isDefaultPrevented: { value: () => defaultPrevented },\n isPropagationStopped: { value: () => propagationStopped },\n preventDefault: {\n value: () => {\n defaultPrevented = true\n nativeEvent.preventDefault()\n },\n },\n stopPropagation: {\n value: () => {\n propagationStopped = true\n nativeEvent.stopPropagation()\n },\n },\n })\n\n return syntheticEvent as MenuSyntheticEvent\n}\n\nfunction isDirectPropertyKey(key: string) {\n return DIRECT_PROPERTY_KEYS.has(key)\n}\n\nfunction setDirectProperty(element: HTMLDivElement, key: string, value: unknown) {\n if (key === 'tabIndex') {\n element.tabIndex = Number(value)\n return\n }\n\n ;(element as unknown as Record<string, unknown>)[key] = value\n}\n\nfunction clearDirectProperty(element: HTMLDivElement, key: string) {\n if (key === 'tabIndex') {\n element.removeAttribute('tabindex')\n return\n }\n\n const propertyValue = (element as unknown as Record<string, unknown>)[key]\n\n if (typeof propertyValue === 'boolean') {\n ;(element as unknown as Record<string, unknown>)[key] = false\n return\n }\n\n if (typeof propertyValue === 'number') {\n ;(element as unknown as Record<string, unknown>)[key] = 0\n return\n }\n\n ;(element as unknown as Record<string, unknown>)[key] = ''\n}\n\nfunction toStyleValue(styleName: string, value: string | number) {\n if (\n typeof value !== 'number' ||\n value === 0 ||\n styleName.startsWith('--') ||\n UNITLESS_STYLE_PROPERTIES.has(styleName)\n ) {\n return String(value)\n }\n\n return `${value}px`\n}\n\nfunction removeStyleProperty(element: HTMLDivElement, styleName: string) {\n if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {\n return\n }\n\n element.style.removeProperty(toStylePropertyName(styleName))\n}\n\nfunction applyStyleProperty(element: HTMLDivElement, styleName: string, value: string | number) {\n if (PLUGIN_MANAGED_STYLE_PROPERTIES.has(styleName)) {\n return\n }\n\n element.style.setProperty(toStylePropertyName(styleName), toStyleValue(styleName, value))\n}\n\nfunction syncAttributes(\n element: HTMLDivElement,\n prevProps: MenuElementProps,\n nextProps: MenuElementProps,\n) {\n const allKeys = new Set([...Object.keys(prevProps), ...Object.keys(nextProps)])\n\n allKeys.forEach(key => {\n if (\n ATTRIBUTE_EXCLUSIONS.has(key) ||\n !isForwardedAttributeKey(key) ||\n isEventProp(key, prevProps[key as keyof MenuElementProps]) ||\n isEventProp(key, nextProps[key as keyof MenuElementProps])\n ) {\n return\n }\n\n const prevValue = prevProps[key as keyof MenuElementProps]\n const nextValue = nextProps[key as keyof MenuElementProps]\n\n if (prevValue === nextValue) {\n return\n }\n\n const attributeName = toAttributeName(key)\n\n if (nextValue == null || nextValue === false) {\n if (isDirectPropertyKey(key)) {\n clearDirectProperty(element, key)\n }\n\n element.removeAttribute(attributeName)\n return\n }\n\n if (nextValue === true) {\n if (isDirectPropertyKey(key)) {\n setDirectProperty(element, key, true)\n }\n\n element.setAttribute(attributeName, '')\n return\n }\n\n if (isDirectPropertyKey(key)) {\n setDirectProperty(element, key, nextValue)\n return\n }\n\n element.setAttribute(attributeName, String(nextValue))\n })\n}\n\nfunction syncClassName(element: HTMLDivElement, prevClassName?: string, nextClassName?: string) {\n if (prevClassName === nextClassName) {\n return\n }\n\n if (nextClassName) {\n element.className = nextClassName\n return\n }\n\n element.removeAttribute('class')\n}\n\nfunction syncStyles(\n element: HTMLDivElement,\n prevStyle: CSSProperties | undefined,\n nextStyle: CSSProperties | undefined,\n) {\n const previousStyle = prevStyle ?? {}\n const currentStyle = nextStyle ?? {}\n const allStyleNames = new Set([...Object.keys(previousStyle), ...Object.keys(currentStyle)])\n\n allStyleNames.forEach(styleName => {\n const prevValue = previousStyle[styleName as keyof CSSProperties]\n const nextValue = currentStyle[styleName as keyof CSSProperties]\n\n if (prevValue === nextValue) {\n return\n }\n\n if (nextValue == null) {\n removeStyleProperty(element, styleName)\n return\n }\n\n applyStyleProperty(element, styleName, nextValue as string | number)\n })\n}\n\nfunction syncEventListeners(\n element: HTMLDivElement,\n prevListeners: EventListenerEntry[],\n nextProps: MenuElementProps,\n) {\n prevListeners.forEach(({ eventName, listener, options }) => {\n element.removeEventListener(eventName, listener, options)\n })\n\n const nextListeners: EventListenerEntry[] = []\n\n Object.entries(nextProps).forEach(([key, value]) => {\n if (!isEventProp(key, value)) {\n return\n }\n\n const { eventName, options } = toEventConfig(key)\n const listener: MenuNativeListener = event => {\n value(createSyntheticEvent(element, event))\n }\n\n element.addEventListener(eventName, listener, options)\n nextListeners.push({ eventName, listener, options })\n })\n\n return nextListeners\n}\n\nexport function useMenuElementProps(element: HTMLDivElement, props: MenuElementProps) {\n const previousPropsRef = useRef<MenuElementProps>({})\n const listenersRef = useRef<EventListenerEntry[]>([])\n\n useIsomorphicLayoutEffect(() => {\n const previousProps = previousPropsRef.current\n\n syncClassName(element, previousProps.className, props.className)\n syncStyles(element, previousProps.style, props.style)\n syncAttributes(element, previousProps, props)\n listenersRef.current = syncEventListeners(element, listenersRef.current, props)\n previousPropsRef.current = props\n\n return () => {\n listenersRef.current.forEach(({ eventName, listener, options }) => {\n element.removeEventListener(eventName, listener, options)\n })\n listenersRef.current = []\n }\n }, [element, props])\n}\n","import { type BubbleMenuPluginProps, BubbleMenuPlugin } from '@tiptap/extension-bubble-menu'\nimport type { PluginKey } from '@tiptap/pm/state'\nimport { useCurrentEditor } from '@tiptap/react'\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getAutoPluginKey } from './getAutoPluginKey.js'\nimport { useMenuElementProps } from './useMenuElementProps.js'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type BubbleMenuProps = Optional<\n Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>,\n 'editor'\n> &\n React.HTMLAttributes<HTMLDivElement>\n\nexport const BubbleMenu = React.forwardRef<HTMLDivElement, BubbleMenuProps>(\n (\n {\n pluginKey,\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n getReferencedVirtualElement,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n const resolvedPluginKey = useRef<PluginKey | string>(\n getAutoPluginKey(pluginKey, 'bubbleMenu'),\n ).current\n\n useMenuElementProps(menuEl.current, restProps)\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n /**\n * The editor instance where the bubble menu plugin will be registered.\n */\n const pluginEditor = editor || currentEditor\n\n // Creating a useMemo would be more computationally expensive than just\n // re-creating this object on every render.\n const bubbleMenuPluginProps: Omit<BubbleMenuPluginProps, 'editor' | 'element'> = {\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey: resolvedPluginKey,\n shouldShow,\n getReferencedVirtualElement,\n options,\n }\n /**\n * The props for the bubble menu plugin. They are accessed inside a ref to\n * avoid running the useEffect hook and re-registering the plugin when the\n * props change.\n */\n const bubbleMenuPluginPropsRef = useRef(bubbleMenuPluginProps)\n bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps\n\n /**\n * Track whether the plugin has been initialized, so we only send updates\n * after the initial registration.\n */\n const [pluginInitialized, setPluginInitialized] = useState(false)\n\n /**\n * Track whether we need to skip the first options update dispatch.\n * This prevents unnecessary updates right after plugin initialization.\n */\n const skipFirstUpdateRef = useRef(true)\n\n useEffect(() => {\n if (pluginEditor?.isDestroyed) {\n return\n }\n\n if (!pluginEditor) {\n console.warn(\n 'BubbleMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const bubbleMenuElement = menuEl.current\n bubbleMenuElement.style.visibility = 'hidden'\n bubbleMenuElement.style.position = 'absolute'\n\n const plugin = BubbleMenuPlugin({\n ...bubbleMenuPluginPropsRef.current,\n editor: pluginEditor,\n element: bubbleMenuElement,\n })\n\n pluginEditor.registerPlugin(plugin)\n\n const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey\n\n skipFirstUpdateRef.current = true\n setPluginInitialized(true)\n\n return () => {\n setPluginInitialized(false)\n pluginEditor.unregisterPlugin(createdPluginKey)\n window.requestAnimationFrame(() => {\n if (bubbleMenuElement.parentNode) {\n bubbleMenuElement.parentNode.removeChild(bubbleMenuElement)\n }\n })\n }\n }, [pluginEditor])\n\n /**\n * Update the plugin options when props change after the plugin has been initialized.\n * This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.\n */\n useEffect(() => {\n if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {\n return\n }\n\n // Skip the first update right after initialization since the plugin was just created with these options\n if (skipFirstUpdateRef.current) {\n skipFirstUpdateRef.current = false\n return\n }\n\n pluginEditor.view.dispatch(\n pluginEditor.state.tr.setMeta(resolvedPluginKey, {\n type: 'updateOptions',\n options: bubbleMenuPluginPropsRef.current,\n }),\n )\n }, [\n pluginInitialized,\n pluginEditor,\n updateDelay,\n resizeDelay,\n shouldShow,\n options,\n appendTo,\n getReferencedVirtualElement,\n resolvedPluginKey,\n ])\n\n return createPortal(children, menuEl.current)\n },\n)\n","import type { FloatingMenuPluginProps } from '@tiptap/extension-floating-menu'\nimport { FloatingMenuPlugin } from '@tiptap/extension-floating-menu'\nimport type { PluginKey } from '@tiptap/pm/state'\nimport { useCurrentEditor } from '@tiptap/react'\nimport React, { useEffect, useRef, useState } from 'react'\nimport { createPortal } from 'react-dom'\n\nimport { getAutoPluginKey } from './getAutoPluginKey.js'\nimport { useMenuElementProps } from './useMenuElementProps.js'\n\ntype Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>\n\nexport type FloatingMenuProps = Omit<\n Optional<FloatingMenuPluginProps, 'pluginKey'>,\n 'element' | 'editor'\n> & {\n editor: FloatingMenuPluginProps['editor'] | null\n options?: FloatingMenuPluginProps['options']\n} & React.HTMLAttributes<HTMLDivElement>\n\nexport const FloatingMenu = React.forwardRef<HTMLDivElement, FloatingMenuProps>(\n (\n {\n pluginKey,\n editor,\n updateDelay,\n resizeDelay,\n appendTo,\n shouldShow = null,\n options,\n children,\n ...restProps\n },\n ref,\n ) => {\n const menuEl = useRef(document.createElement('div'))\n const resolvedPluginKey = useRef<PluginKey | string>(\n getAutoPluginKey(pluginKey, 'floatingMenu'),\n ).current\n\n useMenuElementProps(menuEl.current, restProps)\n\n if (typeof ref === 'function') {\n ref(menuEl.current)\n } else if (ref) {\n ref.current = menuEl.current\n }\n\n const { editor: currentEditor } = useCurrentEditor()\n\n /**\n * The editor instance where the floating menu plugin will be registered.\n */\n const pluginEditor = editor || currentEditor\n\n // Creating a useMemo would be more computationally expensive than just\n // re-creating this object on every render.\n const floatingMenuPluginProps: Omit<FloatingMenuPluginProps, 'editor' | 'element'> = {\n updateDelay,\n resizeDelay,\n appendTo,\n pluginKey: resolvedPluginKey,\n shouldShow,\n options,\n }\n\n /**\n * The props for the floating menu plugin. They are accessed inside a ref to\n * avoid running the useEffect hook and re-registering the plugin when the\n * props change.\n */\n const floatingMenuPluginPropsRef = useRef(floatingMenuPluginProps)\n floatingMenuPluginPropsRef.current = floatingMenuPluginProps\n\n /**\n * Track whether the plugin has been initialized, so we only send updates\n * after the initial registration.\n */\n const [pluginInitialized, setPluginInitialized] = useState(false)\n\n /**\n * Track whether we need to skip the first options update dispatch.\n * This prevents unnecessary updates right after plugin initialization.\n */\n const skipFirstUpdateRef = useRef(true)\n\n useEffect(() => {\n if (pluginEditor?.isDestroyed) {\n return\n }\n\n if (!pluginEditor) {\n console.warn(\n 'FloatingMenu component is not rendered inside of an editor component or does not have editor prop.',\n )\n return\n }\n\n const floatingMenuElement = menuEl.current\n floatingMenuElement.style.visibility = 'hidden'\n floatingMenuElement.style.position = 'absolute'\n\n const plugin = FloatingMenuPlugin({\n ...floatingMenuPluginPropsRef.current,\n editor: pluginEditor,\n element: floatingMenuElement,\n })\n\n pluginEditor.registerPlugin(plugin)\n\n const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey\n\n skipFirstUpdateRef.current = true\n setPluginInitialized(true)\n\n return () => {\n setPluginInitialized(false)\n pluginEditor.unregisterPlugin(createdPluginKey)\n window.requestAnimationFrame(() => {\n if (floatingMenuElement.parentNode) {\n floatingMenuElement.parentNode.removeChild(floatingMenuElement)\n }\n })\n }\n }, [pluginEditor])\n\n /**\n * Update the plugin options when props change after the plugin has been initialized.\n * This allows dynamic updates to options like scrollTarget without re-registering the entire plugin.\n */\n useEffect(() => {\n if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {\n return\n }\n\n // Skip the first update right after initialization since the plugin was just created with these options\n if (skipFirstUpdateRef.current) {\n skipFirstUpdateRef.current = false\n return\n }\n\n pluginEditor.view.dispatch(\n pluginEditor.state.tr.setMeta(resolvedPluginKey, {\n type: 'updateOptions',\n options: floatingMenuPluginPropsRef.current,\n }),\n )\n }, [\n pluginInitialized,\n pluginEditor,\n updateDelay,\n resizeDelay,\n shouldShow,\n options,\n appendTo,\n resolvedPluginKey,\n ])\n\n return createPortal(children, menuEl.current)\n },\n)\n"],"mappings":"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"}
{
"name": "@tiptap/react",
"version": "3.30.2",
"version": "3.30.3",
"description": "React components for tiptap",

@@ -60,4 +60,4 @@ "keywords": [

"react-dom": "^19.0.0",
"@tiptap/core": "^3.30.2",
"@tiptap/pm": "^3.30.2"
"@tiptap/core": "^3.30.3",
"@tiptap/pm": "^3.30.3"
},

@@ -69,12 +69,12 @@ "peerDependencies": {

"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0",
"@tiptap/core": "3.30.2",
"@tiptap/pm": "3.30.2"
"@tiptap/core": "3.30.3",
"@tiptap/pm": "3.30.3"
},
"optionalDependencies": {
"@tiptap/extension-bubble-menu": "^3.30.2",
"@tiptap/extension-floating-menu": "^3.30.2"
"@tiptap/extension-bubble-menu": "^3.30.3",
"@tiptap/extension-floating-menu": "^3.30.3"
},
"scripts": {
"build": "tsup"
"build": "vp pack"
}
}

@@ -1,2 +0,2 @@

import { afterEach, describe, expect, it } from 'vitest'
import { afterEach, describe, expect, it } from 'vite-plus/test'

@@ -3,0 +3,0 @@ import { captureDOMSelection } from './captureDOMSelection.js'

import React from 'react'
import { describe, expect, it, vi } from 'vitest'
import { describe, expect, it, vi } from 'vite-plus/test'

@@ -4,0 +4,0 @@ import { createContentComponent } from './EditorContent.js'

import { render } from '@testing-library/react'
import React, { createRef } from 'react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vite-plus/test'

@@ -5,0 +5,0 @@ import { BubbleMenu } from './BubbleMenu.js'

import { render } from '@testing-library/react'
import React, { createRef } from 'react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vite-plus/test'

@@ -5,0 +5,0 @@ import { FloatingMenu } from './FloatingMenu.js'

import { act, render } from '@testing-library/react'
import { Editor, Node } from '@tiptap/core'
import type { NodeViewRendererProps } from '@tiptap/core'
import type { NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
import Document from '@tiptap/extension-document'

@@ -7,3 +9,3 @@ import Paragraph from '@tiptap/extension-paragraph'

import React, { useEffect, useState } from 'react'
import { afterEach, describe, expect, it } from 'vitest'
import { afterEach, describe, expect, it } from 'vite-plus/test'

@@ -217,2 +219,30 @@ import { EditorContent } from './EditorContent.js'

it('returns a valid fallback node view before EditorContent initializes', () => {
let fallbackNodeView: ProseMirrorNodeView | undefined
const FallbackParagraph = Paragraph.extend({
addNodeView() {
const renderNodeView = ReactNodeViewRenderer(ReactParagraphComponent)
return (props: NodeViewRendererProps) => {
fallbackNodeView = renderNodeView(props)
return fallbackNodeView
}
},
})
let editor: Editor | undefined
expect(() => {
editor = new Editor({
extensions: [Document, FallbackParagraph, Text],
content: '<p>Hello</p>',
})
}).not.toThrow()
expect(fallbackNodeView?.dom).toBeInstanceOf(HTMLElement)
expect(fallbackNodeView?.contentDOM).toBeNull()
expect((fallbackNodeView?.update as (() => boolean) | undefined)?.()).toBe(false)
expect(() => editor?.destroy()).not.toThrow()
})
it('renders nested node views and resolves getPos during render', async () => {

@@ -219,0 +249,0 @@ const editor = createEditorWithContainers()

@@ -470,5 +470,17 @@ import type {

// this is important for vue devtools to show the component hierarchy correctly
// maybe it’s `undefined` because <editor-content> isn’t rendered yet
// maybe it's `undefined` because <editor-content> isn't rendered yet
if (!(props.editor as EditorWithContentComponent).contentComponent) {
return {} as unknown as ProseMirrorNodeView
// Return a minimal valid NodeView with a placeholder DOM element
// to prevent ProseMirror from crashing when calling hasAttribute on undefined
const placeholder = document.createElement('span')
return {
dom: placeholder,
contentDOM: null,
update: () => false,
destroy: () => {},
selectNode: () => {},
deselectNode: () => {},
stopEvent: () => false,
ignoreMutation: () => true,
} as unknown as ProseMirrorNodeView
}

@@ -475,0 +487,0 @@

@@ -8,3 +8,3 @@ import { Editor, Extension } from '@tiptap/core'

import React from 'react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { afterEach, describe, expect, it, vi } from 'vite-plus/test'

@@ -11,0 +11,0 @@ import { EditorContent } from './EditorContent.js'

@@ -7,3 +7,3 @@ /**

import { describe, expect, it } from 'vitest'
import { describe, expect, it } from 'vite-plus/test'

@@ -10,0 +10,0 @@ // The package boundary must stay a client module so it can be imported from

@@ -8,3 +8,3 @@ import { act, render } from '@testing-library/react'

import React from 'react'
import { afterEach, describe, expect, it } from 'vitest'
import { afterEach, describe, expect, it } from 'vite-plus/test'

@@ -11,0 +11,0 @@ import { useEditorState } from './useEditorState.js'

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display