@tiptap/react 3.17.0 → 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -47,10 +47,19 @@ __export(index_exports, {
47
47
  ReactNodeViewContext: () => ReactNodeViewContext,
48
48
  ReactNodeViewRenderer: () => ReactNodeViewRenderer,
49
49
  ReactRenderer: () => ReactRenderer,
50
+ Tiptap: () => Tiptap,
51
+ TiptapBubbleMenu: () => TiptapBubbleMenu,
52
+ TiptapContent: () => TiptapContent,
53
+ TiptapContext: () => TiptapContext,
54
+ TiptapFloatingMenu: () => TiptapFloatingMenu,
55
+ TiptapLoading: () => TiptapLoading,
56
+ TiptapWrapper: () => TiptapWrapper,
50
57
  useCurrentEditor: () => useCurrentEditor,
51
58
  useEditor: () => useEditor,
52
59
  useEditorState: () => useEditorState,
53
- useReactNodeView: () => useReactNodeView
60
+ useReactNodeView: () => useReactNodeView,
61
+ useTiptap: () => useTiptap,
62
+ useTiptapState: () => useTiptapState
54
63
  });
55
64
  module.exports = __toCommonJS(index_exports);
56
65
 
@@ -871,6 +880,7 @@ var ReactNodeView = class extends import_core3.NodeView {
871
880
  * The requestAnimationFrame ID used for selection updates.
872
881
  */
873
882
  this.selectionRafId = null;
883
+ this.cachedExtensionWithSyncedStorage = null;
874
884
  if (!this.node.isLeaf) {
875
885
  if (this.options.contentDOMElementTag) {
876
886
  this.contentDOMElement = document.createElement(this.options.contentDOMElementTag);
@@ -887,6 +897,27 @@ var ReactNodeView = class extends import_core3.NodeView {
887
897
  contentTarget.appendChild(this.contentDOMElement);
888
898
  }
889
899
  }
900
+ /**
901
+ * Returns a proxy of the extension that redirects storage access to the editor's mutable storage.
902
+ * This preserves the original prototype chain (instanceof checks, methods like configure/extend work).
903
+ * Cached to avoid proxy creation on every update.
904
+ */
905
+ get extensionWithSyncedStorage() {
906
+ if (!this.cachedExtensionWithSyncedStorage) {
907
+ const editor = this.editor;
908
+ const extension = this.extension;
909
+ this.cachedExtensionWithSyncedStorage = new Proxy(extension, {
910
+ get(target, prop, receiver) {
911
+ var _a;
912
+ if (prop === "storage") {
913
+ return (_a = editor.storage[extension.name]) != null ? _a : {};
914
+ }
915
+ return Reflect.get(target, prop, receiver);
916
+ }
917
+ });
918
+ }
919
+ return this.cachedExtensionWithSyncedStorage;
920
+ }
890
921
  /**
891
922
  * Setup the React component.
892
923
  * Called on initialization.
@@ -899,7 +930,7 @@ var ReactNodeView = class extends import_core3.NodeView {
899
930
  innerDecorations: this.innerDecorations,
900
931
  view: this.view,
901
932
  selected: false,
902
- extension: this.extension,
933
+ extension: this.extensionWithSyncedStorage,
903
934
  HTMLAttributes: this.HTMLAttributes,
904
935
  getPos: () => this.getPos(),
905
936
  updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
@@ -1020,7 +1051,7 @@ var ReactNodeView = class extends import_core3.NodeView {
1020
1051
  newDecorations: decorations,
1021
1052
  oldInnerDecorations,
1022
1053
  innerDecorations,
1023
- updateProps: () => rerenderComponent({ node, decorations, innerDecorations })
1054
+ updateProps: () => rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage })
1024
1055
  });
1025
1056
  }
1026
1057
  if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
@@ -1029,7 +1060,7 @@ var ReactNodeView = class extends import_core3.NodeView {
1029
1060
  this.node = node;
1030
1061
  this.decorations = decorations;
1031
1062
  this.innerDecorations = innerDecorations;
1032
- rerenderComponent({ node, decorations, innerDecorations });
1063
+ rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage });
1033
1064
  return true;
1034
1065
  }
1035
1066
  /**
@@ -1091,6 +1122,289 @@ function ReactNodeViewRenderer(component, options) {
1091
1122
  };
1092
1123
  }
1093
1124
 
1125
+ // src/Tiptap.tsx
1126
+ var import_react14 = require("react");
1127
+
1128
+ // src/menus/BubbleMenu.tsx
1129
+ var import_extension_bubble_menu = require("@tiptap/extension-bubble-menu");
1130
+ var import_react10 = require("@tiptap/react");
1131
+ var import_react11 = __toESM(require("react"), 1);
1132
+ var import_react_dom3 = require("react-dom");
1133
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1134
+ var BubbleMenu = import_react11.default.forwardRef(
1135
+ ({
1136
+ pluginKey = "bubbleMenu",
1137
+ editor,
1138
+ updateDelay,
1139
+ resizeDelay,
1140
+ appendTo,
1141
+ shouldShow = null,
1142
+ getReferencedVirtualElement,
1143
+ options,
1144
+ children,
1145
+ ...restProps
1146
+ }, ref) => {
1147
+ const menuEl = (0, import_react11.useRef)(document.createElement("div"));
1148
+ if (typeof ref === "function") {
1149
+ ref(menuEl.current);
1150
+ } else if (ref) {
1151
+ ref.current = menuEl.current;
1152
+ }
1153
+ const { editor: currentEditor } = (0, import_react10.useCurrentEditor)();
1154
+ const pluginEditor = editor || currentEditor;
1155
+ const bubbleMenuPluginProps = {
1156
+ updateDelay,
1157
+ resizeDelay,
1158
+ appendTo,
1159
+ pluginKey,
1160
+ shouldShow,
1161
+ getReferencedVirtualElement,
1162
+ options
1163
+ };
1164
+ const bubbleMenuPluginPropsRef = (0, import_react11.useRef)(bubbleMenuPluginProps);
1165
+ bubbleMenuPluginPropsRef.current = bubbleMenuPluginProps;
1166
+ const [pluginInitialized, setPluginInitialized] = (0, import_react11.useState)(false);
1167
+ const skipFirstUpdateRef = (0, import_react11.useRef)(true);
1168
+ (0, import_react11.useEffect)(() => {
1169
+ if (pluginEditor == null ? void 0 : pluginEditor.isDestroyed) {
1170
+ return;
1171
+ }
1172
+ if (!pluginEditor) {
1173
+ console.warn("BubbleMenu component is not rendered inside of an editor component or does not have editor prop.");
1174
+ return;
1175
+ }
1176
+ const bubbleMenuElement = menuEl.current;
1177
+ bubbleMenuElement.style.visibility = "hidden";
1178
+ bubbleMenuElement.style.position = "absolute";
1179
+ const plugin = (0, import_extension_bubble_menu.BubbleMenuPlugin)({
1180
+ ...bubbleMenuPluginPropsRef.current,
1181
+ editor: pluginEditor,
1182
+ element: bubbleMenuElement
1183
+ });
1184
+ pluginEditor.registerPlugin(plugin);
1185
+ const createdPluginKey = bubbleMenuPluginPropsRef.current.pluginKey;
1186
+ skipFirstUpdateRef.current = true;
1187
+ setPluginInitialized(true);
1188
+ return () => {
1189
+ setPluginInitialized(false);
1190
+ pluginEditor.unregisterPlugin(createdPluginKey);
1191
+ window.requestAnimationFrame(() => {
1192
+ if (bubbleMenuElement.parentNode) {
1193
+ bubbleMenuElement.parentNode.removeChild(bubbleMenuElement);
1194
+ }
1195
+ });
1196
+ };
1197
+ }, [pluginEditor]);
1198
+ (0, import_react11.useEffect)(() => {
1199
+ if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {
1200
+ return;
1201
+ }
1202
+ if (skipFirstUpdateRef.current) {
1203
+ skipFirstUpdateRef.current = false;
1204
+ return;
1205
+ }
1206
+ pluginEditor.view.dispatch(
1207
+ pluginEditor.state.tr.setMeta("bubbleMenu", {
1208
+ type: "updateOptions",
1209
+ options: bubbleMenuPluginPropsRef.current
1210
+ })
1211
+ );
1212
+ }, [
1213
+ pluginInitialized,
1214
+ pluginEditor,
1215
+ updateDelay,
1216
+ resizeDelay,
1217
+ shouldShow,
1218
+ options,
1219
+ appendTo,
1220
+ getReferencedVirtualElement
1221
+ ]);
1222
+ return (0, import_react_dom3.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ...restProps, children }), menuEl.current);
1223
+ }
1224
+ );
1225
+
1226
+ // src/menus/FloatingMenu.tsx
1227
+ var import_extension_floating_menu = require("@tiptap/extension-floating-menu");
1228
+ var import_react12 = require("@tiptap/react");
1229
+ var import_react13 = __toESM(require("react"), 1);
1230
+ var import_react_dom4 = require("react-dom");
1231
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1232
+ var FloatingMenu = import_react13.default.forwardRef(
1233
+ ({
1234
+ pluginKey = "floatingMenu",
1235
+ editor,
1236
+ updateDelay,
1237
+ resizeDelay,
1238
+ appendTo,
1239
+ shouldShow = null,
1240
+ options,
1241
+ children,
1242
+ ...restProps
1243
+ }, ref) => {
1244
+ const menuEl = (0, import_react13.useRef)(document.createElement("div"));
1245
+ if (typeof ref === "function") {
1246
+ ref(menuEl.current);
1247
+ } else if (ref) {
1248
+ ref.current = menuEl.current;
1249
+ }
1250
+ const { editor: currentEditor } = (0, import_react12.useCurrentEditor)();
1251
+ const pluginEditor = editor || currentEditor;
1252
+ const floatingMenuPluginProps = {
1253
+ updateDelay,
1254
+ resizeDelay,
1255
+ appendTo,
1256
+ pluginKey,
1257
+ shouldShow,
1258
+ options
1259
+ };
1260
+ const floatingMenuPluginPropsRef = (0, import_react13.useRef)(floatingMenuPluginProps);
1261
+ floatingMenuPluginPropsRef.current = floatingMenuPluginProps;
1262
+ const [pluginInitialized, setPluginInitialized] = (0, import_react13.useState)(false);
1263
+ const skipFirstUpdateRef = (0, import_react13.useRef)(true);
1264
+ (0, import_react13.useEffect)(() => {
1265
+ if (pluginEditor == null ? void 0 : pluginEditor.isDestroyed) {
1266
+ return;
1267
+ }
1268
+ if (!pluginEditor) {
1269
+ console.warn(
1270
+ "FloatingMenu component is not rendered inside of an editor component or does not have editor prop."
1271
+ );
1272
+ return;
1273
+ }
1274
+ const floatingMenuElement = menuEl.current;
1275
+ floatingMenuElement.style.visibility = "hidden";
1276
+ floatingMenuElement.style.position = "absolute";
1277
+ const plugin = (0, import_extension_floating_menu.FloatingMenuPlugin)({
1278
+ ...floatingMenuPluginPropsRef.current,
1279
+ editor: pluginEditor,
1280
+ element: floatingMenuElement
1281
+ });
1282
+ pluginEditor.registerPlugin(plugin);
1283
+ const createdPluginKey = floatingMenuPluginPropsRef.current.pluginKey;
1284
+ skipFirstUpdateRef.current = true;
1285
+ setPluginInitialized(true);
1286
+ return () => {
1287
+ setPluginInitialized(false);
1288
+ pluginEditor.unregisterPlugin(createdPluginKey);
1289
+ window.requestAnimationFrame(() => {
1290
+ if (floatingMenuElement.parentNode) {
1291
+ floatingMenuElement.parentNode.removeChild(floatingMenuElement);
1292
+ }
1293
+ });
1294
+ };
1295
+ }, [pluginEditor]);
1296
+ (0, import_react13.useEffect)(() => {
1297
+ if (!pluginInitialized || !pluginEditor || pluginEditor.isDestroyed) {
1298
+ return;
1299
+ }
1300
+ if (skipFirstUpdateRef.current) {
1301
+ skipFirstUpdateRef.current = false;
1302
+ return;
1303
+ }
1304
+ pluginEditor.view.dispatch(
1305
+ pluginEditor.state.tr.setMeta("floatingMenu", {
1306
+ type: "updateOptions",
1307
+ options: floatingMenuPluginPropsRef.current
1308
+ })
1309
+ );
1310
+ }, [pluginInitialized, pluginEditor, updateDelay, resizeDelay, shouldShow, options, appendTo]);
1311
+ return (0, import_react_dom4.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { ...restProps, children }), menuEl.current);
1312
+ }
1313
+ );
1314
+
1315
+ // src/Tiptap.tsx
1316
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1317
+ var TiptapContext = (0, import_react14.createContext)({
1318
+ editor: null,
1319
+ isReady: false
1320
+ });
1321
+ TiptapContext.displayName = "TiptapContext";
1322
+ var useTiptap = () => (0, import_react14.useContext)(TiptapContext);
1323
+ function useTiptapState(selector, equalityFn) {
1324
+ const { editor } = useTiptap();
1325
+ return useEditorState({
1326
+ editor,
1327
+ selector,
1328
+ equalityFn
1329
+ });
1330
+ }
1331
+ function TiptapWrapper({ instance, children }) {
1332
+ var _a;
1333
+ const [isReady, setIsReady] = (0, import_react14.useState)((_a = instance == null ? void 0 : instance.isInitialized) != null ? _a : false);
1334
+ (0, import_react14.useEffect)(() => {
1335
+ if (!instance) {
1336
+ setIsReady(false);
1337
+ return;
1338
+ }
1339
+ if (instance.isInitialized) {
1340
+ setIsReady(true);
1341
+ return;
1342
+ }
1343
+ const handleCreate = () => {
1344
+ setIsReady(true);
1345
+ };
1346
+ instance.on("create", handleCreate);
1347
+ return () => {
1348
+ instance.off("create", handleCreate);
1349
+ };
1350
+ }, [instance]);
1351
+ const tiptapContextValue = (0, import_react14.useMemo)(() => ({ editor: instance, isReady }), [instance, isReady]);
1352
+ const legacyContextValue = (0, import_react14.useMemo)(() => ({ editor: instance }), [instance]);
1353
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(EditorContext.Provider, { value: legacyContextValue, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TiptapContext.Provider, { value: tiptapContextValue, children }) });
1354
+ }
1355
+ TiptapWrapper.displayName = "Tiptap";
1356
+ function TiptapContent({ ...rest }) {
1357
+ const { editor } = useTiptap();
1358
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(EditorContent, { editor, ...rest });
1359
+ }
1360
+ TiptapContent.displayName = "Tiptap.Content";
1361
+ function TiptapLoading({ children }) {
1362
+ const { isReady } = useTiptap();
1363
+ if (isReady) {
1364
+ return null;
1365
+ }
1366
+ return children;
1367
+ }
1368
+ TiptapLoading.displayName = "Tiptap.Loading";
1369
+ function TiptapBubbleMenu({ children, ...rest }) {
1370
+ const { editor } = useTiptap();
1371
+ if (!editor) {
1372
+ return null;
1373
+ }
1374
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(BubbleMenu, { editor, ...rest, children });
1375
+ }
1376
+ TiptapBubbleMenu.displayName = "Tiptap.BubbleMenu";
1377
+ function TiptapFloatingMenu({ children, ...rest }) {
1378
+ const { editor } = useTiptap();
1379
+ if (!editor) {
1380
+ return null;
1381
+ }
1382
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(FloatingMenu, { ...rest, editor, children });
1383
+ }
1384
+ TiptapFloatingMenu.displayName = "Tiptap.FloatingMenu";
1385
+ var Tiptap = Object.assign(TiptapWrapper, {
1386
+ /**
1387
+ * The Tiptap Content component that renders the EditorContent with the editor instance from the context.
1388
+ * @see TiptapContent
1389
+ */
1390
+ Content: TiptapContent,
1391
+ /**
1392
+ * The Tiptap Loading component that renders its children only when the editor is not ready.
1393
+ * @see TiptapLoading
1394
+ */
1395
+ Loading: TiptapLoading,
1396
+ /**
1397
+ * The Tiptap BubbleMenu component that wraps the BubbleMenu from Tiptap and provides the editor instance from the context.
1398
+ * @see TiptapBubbleMenu
1399
+ */
1400
+ BubbleMenu: TiptapBubbleMenu,
1401
+ /**
1402
+ * The Tiptap FloatingMenu component that wraps the FloatingMenu from Tiptap and provides the editor instance from the context.
1403
+ * @see TiptapFloatingMenu
1404
+ */
1405
+ FloatingMenu: TiptapFloatingMenu
1406
+ });
1407
+
1094
1408
  // src/index.ts
1095
1409
  __reExport(index_exports, require("@tiptap/core"), module.exports);
1096
1410
  // Annotate the CommonJS export names for ESM import in node:
@@ -1111,10 +1425,19 @@ __reExport(index_exports, require("@tiptap/core"), module.exports);
1111
1425
  ReactNodeViewContext,
1112
1426
  ReactNodeViewRenderer,
1113
1427
  ReactRenderer,
1428
+ Tiptap,
1429
+ TiptapBubbleMenu,
1430
+ TiptapContent,
1431
+ TiptapContext,
1432
+ TiptapFloatingMenu,
1433
+ TiptapLoading,
1434
+ TiptapWrapper,
1114
1435
  useCurrentEditor,
1115
1436
  useEditor,
1116
1437
  useEditorState,
1117
1438
  useReactNodeView,
1439
+ useTiptap,
1440
+ useTiptapState,
1118
1441
  ...require("@tiptap/core")
1119
1442
  });
1120
1443
  //# sourceMappingURL=index.cjs.map