@tiptap/react 3.20.1 → 3.20.3

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.js DELETED
@@ -1,1131 +0,0 @@
1
- // src/Context.tsx
2
- import { createContext, useContext, useMemo } from "react";
3
-
4
- // src/EditorContent.tsx
5
- import React, { forwardRef } from "react";
6
- import ReactDOM from "react-dom";
7
- import { useSyncExternalStore } from "use-sync-external-store/shim/index.js";
8
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
- var mergeRefs = (...refs) => {
10
- return (node) => {
11
- refs.forEach((ref) => {
12
- if (typeof ref === "function") {
13
- ref(node);
14
- } else if (ref) {
15
- ;
16
- ref.current = node;
17
- }
18
- });
19
- };
20
- };
21
- var Portals = ({ contentComponent }) => {
22
- const renderers = useSyncExternalStore(
23
- contentComponent.subscribe,
24
- contentComponent.getSnapshot,
25
- contentComponent.getServerSnapshot
26
- );
27
- return /* @__PURE__ */ jsx(Fragment, { children: Object.values(renderers) });
28
- };
29
- function getInstance() {
30
- const subscribers = /* @__PURE__ */ new Set();
31
- let renderers = {};
32
- return {
33
- /**
34
- * Subscribe to the editor instance's changes.
35
- */
36
- subscribe(callback) {
37
- subscribers.add(callback);
38
- return () => {
39
- subscribers.delete(callback);
40
- };
41
- },
42
- getSnapshot() {
43
- return renderers;
44
- },
45
- getServerSnapshot() {
46
- return renderers;
47
- },
48
- /**
49
- * Adds a new NodeView Renderer to the editor.
50
- */
51
- setRenderer(id, renderer) {
52
- renderers = {
53
- ...renderers,
54
- [id]: ReactDOM.createPortal(renderer.reactElement, renderer.element, id)
55
- };
56
- subscribers.forEach((subscriber) => subscriber());
57
- },
58
- /**
59
- * Removes a NodeView Renderer from the editor.
60
- */
61
- removeRenderer(id) {
62
- const nextRenderers = { ...renderers };
63
- delete nextRenderers[id];
64
- renderers = nextRenderers;
65
- subscribers.forEach((subscriber) => subscriber());
66
- }
67
- };
68
- }
69
- var PureEditorContent = class extends React.Component {
70
- constructor(props) {
71
- var _a;
72
- super(props);
73
- this.editorContentRef = React.createRef();
74
- this.initialized = false;
75
- this.state = {
76
- hasContentComponentInitialized: Boolean((_a = props.editor) == null ? void 0 : _a.contentComponent)
77
- };
78
- }
79
- componentDidMount() {
80
- this.init();
81
- }
82
- componentDidUpdate() {
83
- this.init();
84
- }
85
- init() {
86
- var _a;
87
- const editor = this.props.editor;
88
- if (editor && !editor.isDestroyed && ((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) {
89
- if (editor.contentComponent) {
90
- return;
91
- }
92
- const element = this.editorContentRef.current;
93
- element.append(...editor.view.dom.parentNode.childNodes);
94
- editor.setOptions({
95
- element
96
- });
97
- editor.contentComponent = getInstance();
98
- if (!this.state.hasContentComponentInitialized) {
99
- this.unsubscribeToContentComponent = editor.contentComponent.subscribe(() => {
100
- this.setState((prevState) => {
101
- if (!prevState.hasContentComponentInitialized) {
102
- return {
103
- hasContentComponentInitialized: true
104
- };
105
- }
106
- return prevState;
107
- });
108
- if (this.unsubscribeToContentComponent) {
109
- this.unsubscribeToContentComponent();
110
- }
111
- });
112
- }
113
- editor.createNodeViews();
114
- this.initialized = true;
115
- }
116
- }
117
- componentWillUnmount() {
118
- var _a;
119
- const editor = this.props.editor;
120
- if (!editor) {
121
- return;
122
- }
123
- this.initialized = false;
124
- if (!editor.isDestroyed) {
125
- editor.view.setProps({
126
- nodeViews: {}
127
- });
128
- }
129
- if (this.unsubscribeToContentComponent) {
130
- this.unsubscribeToContentComponent();
131
- }
132
- editor.contentComponent = null;
133
- try {
134
- if (!((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) {
135
- return;
136
- }
137
- const newElement = document.createElement("div");
138
- newElement.append(...editor.view.dom.parentNode.childNodes);
139
- editor.setOptions({
140
- element: newElement
141
- });
142
- } catch {
143
- }
144
- }
145
- render() {
146
- const { editor, innerRef, ...rest } = this.props;
147
- return /* @__PURE__ */ jsxs(Fragment, { children: [
148
- /* @__PURE__ */ jsx("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }),
149
- (editor == null ? void 0 : editor.contentComponent) && /* @__PURE__ */ jsx(Portals, { contentComponent: editor.contentComponent })
150
- ] });
151
- }
152
- };
153
- var EditorContentWithKey = forwardRef(
154
- (props, ref) => {
155
- const key = React.useMemo(() => {
156
- return Math.floor(Math.random() * 4294967295).toString();
157
- }, [props.editor]);
158
- return React.createElement(PureEditorContent, {
159
- key,
160
- innerRef: ref,
161
- ...props
162
- });
163
- }
164
- );
165
- var EditorContent = React.memo(EditorContentWithKey);
166
-
167
- // src/useEditor.ts
168
- import { Editor } from "@tiptap/core";
169
- import { useDebugValue as useDebugValue2, useEffect as useEffect2, useRef, useState as useState2 } from "react";
170
- import { useSyncExternalStore as useSyncExternalStore2 } from "use-sync-external-store/shim/index.js";
171
-
172
- // src/useEditorState.ts
173
- import { deepEqual } from "fast-equals";
174
- import { useDebugValue, useEffect, useLayoutEffect, useState } from "react";
175
- import { useSyncExternalStoreWithSelector } from "use-sync-external-store/shim/with-selector.js";
176
- var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
177
- var EditorStateManager = class {
178
- constructor(initialEditor) {
179
- this.transactionNumber = 0;
180
- this.lastTransactionNumber = 0;
181
- this.subscribers = /* @__PURE__ */ new Set();
182
- this.editor = initialEditor;
183
- this.lastSnapshot = { editor: initialEditor, transactionNumber: 0 };
184
- this.getSnapshot = this.getSnapshot.bind(this);
185
- this.getServerSnapshot = this.getServerSnapshot.bind(this);
186
- this.watch = this.watch.bind(this);
187
- this.subscribe = this.subscribe.bind(this);
188
- }
189
- /**
190
- * Get the current editor instance.
191
- */
192
- getSnapshot() {
193
- if (this.transactionNumber === this.lastTransactionNumber) {
194
- return this.lastSnapshot;
195
- }
196
- this.lastTransactionNumber = this.transactionNumber;
197
- this.lastSnapshot = { editor: this.editor, transactionNumber: this.transactionNumber };
198
- return this.lastSnapshot;
199
- }
200
- /**
201
- * Always disable the editor on the server-side.
202
- */
203
- getServerSnapshot() {
204
- return { editor: null, transactionNumber: 0 };
205
- }
206
- /**
207
- * Subscribe to the editor instance's changes.
208
- */
209
- subscribe(callback) {
210
- this.subscribers.add(callback);
211
- return () => {
212
- this.subscribers.delete(callback);
213
- };
214
- }
215
- /**
216
- * Watch the editor instance for changes.
217
- */
218
- watch(nextEditor) {
219
- this.editor = nextEditor;
220
- if (this.editor) {
221
- const fn = () => {
222
- this.transactionNumber += 1;
223
- this.subscribers.forEach((callback) => callback());
224
- };
225
- const currentEditor = this.editor;
226
- currentEditor.on("transaction", fn);
227
- return () => {
228
- currentEditor.off("transaction", fn);
229
- };
230
- }
231
- return void 0;
232
- }
233
- };
234
- function useEditorState(options) {
235
- var _a;
236
- const [editorStateManager] = useState(() => new EditorStateManager(options.editor));
237
- const selectedState = useSyncExternalStoreWithSelector(
238
- editorStateManager.subscribe,
239
- editorStateManager.getSnapshot,
240
- editorStateManager.getServerSnapshot,
241
- options.selector,
242
- (_a = options.equalityFn) != null ? _a : deepEqual
243
- );
244
- useIsomorphicLayoutEffect(() => {
245
- return editorStateManager.watch(options.editor);
246
- }, [options.editor, editorStateManager]);
247
- useDebugValue(selectedState);
248
- return selectedState;
249
- }
250
-
251
- // src/useEditor.ts
252
- var isDev = process.env.NODE_ENV !== "production";
253
- var isSSR = typeof window === "undefined";
254
- var isNext = isSSR || Boolean(typeof window !== "undefined" && window.next);
255
- var EditorInstanceManager = class _EditorInstanceManager {
256
- constructor(options) {
257
- /**
258
- * The current editor instance.
259
- */
260
- this.editor = null;
261
- /**
262
- * The subscriptions to notify when the editor instance
263
- * has been created or destroyed.
264
- */
265
- this.subscriptions = /* @__PURE__ */ new Set();
266
- /**
267
- * Whether the editor has been mounted.
268
- */
269
- this.isComponentMounted = false;
270
- /**
271
- * The most recent dependencies array.
272
- */
273
- this.previousDeps = null;
274
- /**
275
- * The unique instance ID. This is used to identify the editor instance. And will be re-generated for each new instance.
276
- */
277
- this.instanceId = "";
278
- this.options = options;
279
- this.subscriptions = /* @__PURE__ */ new Set();
280
- this.setEditor(this.getInitialEditor());
281
- this.scheduleDestroy();
282
- this.getEditor = this.getEditor.bind(this);
283
- this.getServerSnapshot = this.getServerSnapshot.bind(this);
284
- this.subscribe = this.subscribe.bind(this);
285
- this.refreshEditorInstance = this.refreshEditorInstance.bind(this);
286
- this.scheduleDestroy = this.scheduleDestroy.bind(this);
287
- this.onRender = this.onRender.bind(this);
288
- this.createEditor = this.createEditor.bind(this);
289
- }
290
- setEditor(editor) {
291
- this.editor = editor;
292
- this.instanceId = Math.random().toString(36).slice(2, 9);
293
- this.subscriptions.forEach((cb) => cb());
294
- }
295
- getInitialEditor() {
296
- if (this.options.current.immediatelyRender === void 0) {
297
- if (isSSR || isNext) {
298
- if (isDev) {
299
- throw new Error(
300
- "Tiptap Error: SSR has been detected, please set `immediatelyRender` explicitly to `false` to avoid hydration mismatches."
301
- );
302
- }
303
- return null;
304
- }
305
- return this.createEditor();
306
- }
307
- if (this.options.current.immediatelyRender && isSSR && isDev) {
308
- throw new Error(
309
- "Tiptap Error: SSR has been detected, and `immediatelyRender` has been set to `true` this is an unsupported configuration that may result in errors, explicitly set `immediatelyRender` to `false` to avoid hydration mismatches."
310
- );
311
- }
312
- if (this.options.current.immediatelyRender) {
313
- return this.createEditor();
314
- }
315
- return null;
316
- }
317
- /**
318
- * Create a new editor instance. And attach event listeners.
319
- */
320
- createEditor() {
321
- const optionsToApply = {
322
- ...this.options.current,
323
- // Always call the most recent version of the callback function by default
324
- onBeforeCreate: (...args) => {
325
- var _a, _b;
326
- return (_b = (_a = this.options.current).onBeforeCreate) == null ? void 0 : _b.call(_a, ...args);
327
- },
328
- onBlur: (...args) => {
329
- var _a, _b;
330
- return (_b = (_a = this.options.current).onBlur) == null ? void 0 : _b.call(_a, ...args);
331
- },
332
- onCreate: (...args) => {
333
- var _a, _b;
334
- return (_b = (_a = this.options.current).onCreate) == null ? void 0 : _b.call(_a, ...args);
335
- },
336
- onDestroy: (...args) => {
337
- var _a, _b;
338
- return (_b = (_a = this.options.current).onDestroy) == null ? void 0 : _b.call(_a, ...args);
339
- },
340
- onFocus: (...args) => {
341
- var _a, _b;
342
- return (_b = (_a = this.options.current).onFocus) == null ? void 0 : _b.call(_a, ...args);
343
- },
344
- onSelectionUpdate: (...args) => {
345
- var _a, _b;
346
- return (_b = (_a = this.options.current).onSelectionUpdate) == null ? void 0 : _b.call(_a, ...args);
347
- },
348
- onTransaction: (...args) => {
349
- var _a, _b;
350
- return (_b = (_a = this.options.current).onTransaction) == null ? void 0 : _b.call(_a, ...args);
351
- },
352
- onUpdate: (...args) => {
353
- var _a, _b;
354
- return (_b = (_a = this.options.current).onUpdate) == null ? void 0 : _b.call(_a, ...args);
355
- },
356
- onContentError: (...args) => {
357
- var _a, _b;
358
- return (_b = (_a = this.options.current).onContentError) == null ? void 0 : _b.call(_a, ...args);
359
- },
360
- onDrop: (...args) => {
361
- var _a, _b;
362
- return (_b = (_a = this.options.current).onDrop) == null ? void 0 : _b.call(_a, ...args);
363
- },
364
- onPaste: (...args) => {
365
- var _a, _b;
366
- return (_b = (_a = this.options.current).onPaste) == null ? void 0 : _b.call(_a, ...args);
367
- },
368
- onDelete: (...args) => {
369
- var _a, _b;
370
- return (_b = (_a = this.options.current).onDelete) == null ? void 0 : _b.call(_a, ...args);
371
- }
372
- };
373
- const editor = new Editor(optionsToApply);
374
- return editor;
375
- }
376
- /**
377
- * Get the current editor instance.
378
- */
379
- getEditor() {
380
- return this.editor;
381
- }
382
- /**
383
- * Always disable the editor on the server-side.
384
- */
385
- getServerSnapshot() {
386
- return null;
387
- }
388
- /**
389
- * Subscribe to the editor instance's changes.
390
- */
391
- subscribe(onStoreChange) {
392
- this.subscriptions.add(onStoreChange);
393
- return () => {
394
- this.subscriptions.delete(onStoreChange);
395
- };
396
- }
397
- static compareOptions(a, b) {
398
- return Object.keys(a).every((key) => {
399
- if ([
400
- "onCreate",
401
- "onBeforeCreate",
402
- "onDestroy",
403
- "onUpdate",
404
- "onTransaction",
405
- "onFocus",
406
- "onBlur",
407
- "onSelectionUpdate",
408
- "onContentError",
409
- "onDrop",
410
- "onPaste"
411
- ].includes(key)) {
412
- return true;
413
- }
414
- if (key === "extensions" && a.extensions && b.extensions) {
415
- if (a.extensions.length !== b.extensions.length) {
416
- return false;
417
- }
418
- return a.extensions.every((extension, index) => {
419
- var _a;
420
- if (extension !== ((_a = b.extensions) == null ? void 0 : _a[index])) {
421
- return false;
422
- }
423
- return true;
424
- });
425
- }
426
- if (a[key] !== b[key]) {
427
- return false;
428
- }
429
- return true;
430
- });
431
- }
432
- /**
433
- * On each render, we will create, update, or destroy the editor instance.
434
- * @param deps The dependencies to watch for changes
435
- * @returns A cleanup function
436
- */
437
- onRender(deps) {
438
- return () => {
439
- this.isComponentMounted = true;
440
- clearTimeout(this.scheduledDestructionTimeout);
441
- if (this.editor && !this.editor.isDestroyed && deps.length === 0) {
442
- if (!_EditorInstanceManager.compareOptions(this.options.current, this.editor.options)) {
443
- this.editor.setOptions({
444
- ...this.options.current,
445
- editable: this.editor.isEditable
446
- });
447
- }
448
- } else {
449
- this.refreshEditorInstance(deps);
450
- }
451
- return () => {
452
- this.isComponentMounted = false;
453
- this.scheduleDestroy();
454
- };
455
- };
456
- }
457
- /**
458
- * Recreate the editor instance if the dependencies have changed.
459
- */
460
- refreshEditorInstance(deps) {
461
- if (this.editor && !this.editor.isDestroyed) {
462
- if (this.previousDeps === null) {
463
- this.previousDeps = deps;
464
- return;
465
- }
466
- const depsAreEqual = this.previousDeps.length === deps.length && this.previousDeps.every((dep, index) => dep === deps[index]);
467
- if (depsAreEqual) {
468
- return;
469
- }
470
- }
471
- if (this.editor && !this.editor.isDestroyed) {
472
- this.editor.destroy();
473
- }
474
- this.setEditor(this.createEditor());
475
- this.previousDeps = deps;
476
- }
477
- /**
478
- * Schedule the destruction of the editor instance.
479
- * This will only destroy the editor if it was not mounted on the next tick.
480
- * This is to avoid destroying the editor instance when it's actually still mounted.
481
- */
482
- scheduleDestroy() {
483
- const currentInstanceId = this.instanceId;
484
- const currentEditor = this.editor;
485
- this.scheduledDestructionTimeout = setTimeout(() => {
486
- if (this.isComponentMounted && this.instanceId === currentInstanceId) {
487
- if (currentEditor) {
488
- currentEditor.setOptions(this.options.current);
489
- }
490
- return;
491
- }
492
- if (currentEditor && !currentEditor.isDestroyed) {
493
- currentEditor.destroy();
494
- if (this.instanceId === currentInstanceId) {
495
- this.setEditor(null);
496
- }
497
- }
498
- }, 1);
499
- }
500
- };
501
- function useEditor(options = {}, deps = []) {
502
- const mostRecentOptions = useRef(options);
503
- mostRecentOptions.current = options;
504
- const [instanceManager] = useState2(() => new EditorInstanceManager(mostRecentOptions));
505
- const editor = useSyncExternalStore2(
506
- instanceManager.subscribe,
507
- instanceManager.getEditor,
508
- instanceManager.getServerSnapshot
509
- );
510
- useDebugValue2(editor);
511
- useEffect2(instanceManager.onRender(deps));
512
- useEditorState({
513
- editor,
514
- selector: ({ transactionNumber }) => {
515
- if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === void 0) {
516
- return null;
517
- }
518
- if (options.immediatelyRender && transactionNumber === 0) {
519
- return 0;
520
- }
521
- return transactionNumber + 1;
522
- }
523
- });
524
- return editor;
525
- }
526
-
527
- // src/Context.tsx
528
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
529
- var EditorContext = createContext({
530
- editor: null
531
- });
532
- var EditorConsumer = EditorContext.Consumer;
533
- var useCurrentEditor = () => useContext(EditorContext);
534
- function EditorProvider({
535
- children,
536
- slotAfter,
537
- slotBefore,
538
- editorContainerProps = {},
539
- ...editorOptions
540
- }) {
541
- const editor = useEditor(editorOptions);
542
- const contextValue = useMemo(() => ({ editor }), [editor]);
543
- if (!editor) {
544
- return null;
545
- }
546
- return /* @__PURE__ */ jsxs2(EditorContext.Provider, { value: contextValue, children: [
547
- slotBefore,
548
- /* @__PURE__ */ jsx2(EditorConsumer, { children: ({ editor: currentEditor }) => /* @__PURE__ */ jsx2(EditorContent, { editor: currentEditor, ...editorContainerProps }) }),
549
- children,
550
- slotAfter
551
- ] });
552
- }
553
-
554
- // src/useReactNodeView.ts
555
- import { createContext as createContext2, createElement, useContext as useContext2 } from "react";
556
- var ReactNodeViewContext = createContext2({
557
- onDragStart: () => {
558
- },
559
- nodeViewContentChildren: void 0,
560
- nodeViewContentRef: () => {
561
- }
562
- });
563
- var ReactNodeViewContentProvider = ({ children, content }) => {
564
- return createElement(ReactNodeViewContext.Provider, { value: { nodeViewContentChildren: content } }, children);
565
- };
566
- var useReactNodeView = () => useContext2(ReactNodeViewContext);
567
-
568
- // src/NodeViewContent.tsx
569
- import { jsx as jsx3 } from "react/jsx-runtime";
570
- function NodeViewContent({
571
- as: Tag = "div",
572
- ...props
573
- }) {
574
- const { nodeViewContentRef, nodeViewContentChildren } = useReactNodeView();
575
- return (
576
- // @ts-ignore
577
- /* @__PURE__ */ jsx3(
578
- Tag,
579
- {
580
- ...props,
581
- ref: nodeViewContentRef,
582
- "data-node-view-content": "",
583
- style: {
584
- whiteSpace: "pre-wrap",
585
- ...props.style
586
- },
587
- children: nodeViewContentChildren
588
- }
589
- )
590
- );
591
- }
592
-
593
- // src/NodeViewWrapper.tsx
594
- import React3 from "react";
595
- import { jsx as jsx4 } from "react/jsx-runtime";
596
- var NodeViewWrapper = React3.forwardRef((props, ref) => {
597
- const { onDragStart } = useReactNodeView();
598
- const Tag = props.as || "div";
599
- return (
600
- // @ts-ignore
601
- /* @__PURE__ */ jsx4(
602
- Tag,
603
- {
604
- ...props,
605
- ref,
606
- "data-node-view-wrapper": "",
607
- onDragStart,
608
- style: {
609
- whiteSpace: "normal",
610
- ...props.style
611
- }
612
- }
613
- )
614
- );
615
- });
616
-
617
- // src/ReactMarkViewRenderer.tsx
618
- import { MarkView } from "@tiptap/core";
619
- import React4 from "react";
620
-
621
- // src/ReactRenderer.tsx
622
- import { version as reactVersion } from "react";
623
- import { flushSync } from "react-dom";
624
- import { jsx as jsx5 } from "react/jsx-runtime";
625
- function isClassComponent(Component) {
626
- return !!(typeof Component === "function" && Component.prototype && Component.prototype.isReactComponent);
627
- }
628
- function isForwardRefComponent(Component) {
629
- return !!(typeof Component === "object" && Component.$$typeof && (Component.$$typeof.toString() === "Symbol(react.forward_ref)" || Component.$$typeof.description === "react.forward_ref"));
630
- }
631
- function isMemoComponent(Component) {
632
- return !!(typeof Component === "object" && Component.$$typeof && (Component.$$typeof.toString() === "Symbol(react.memo)" || Component.$$typeof.description === "react.memo"));
633
- }
634
- function canReceiveRef(Component) {
635
- if (isClassComponent(Component)) {
636
- return true;
637
- }
638
- if (isForwardRefComponent(Component)) {
639
- return true;
640
- }
641
- if (isMemoComponent(Component)) {
642
- const wrappedComponent = Component.type;
643
- if (wrappedComponent) {
644
- return isClassComponent(wrappedComponent) || isForwardRefComponent(wrappedComponent);
645
- }
646
- }
647
- return false;
648
- }
649
- function isReact19Plus() {
650
- try {
651
- if (reactVersion) {
652
- const majorVersion = parseInt(reactVersion.split(".")[0], 10);
653
- return majorVersion >= 19;
654
- }
655
- } catch {
656
- }
657
- return false;
658
- }
659
- var ReactRenderer = class {
660
- /**
661
- * Immediately creates element and renders the provided React component.
662
- */
663
- constructor(component, { editor, props = {}, as = "div", className = "" }) {
664
- this.ref = null;
665
- /**
666
- * Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
667
- */
668
- this.destroyed = false;
669
- this.id = Math.floor(Math.random() * 4294967295).toString();
670
- this.component = component;
671
- this.editor = editor;
672
- this.props = props;
673
- this.element = document.createElement(as);
674
- this.element.classList.add("react-renderer");
675
- if (className) {
676
- this.element.classList.add(...className.split(" "));
677
- }
678
- if (this.editor.isInitialized) {
679
- flushSync(() => {
680
- this.render();
681
- });
682
- } else {
683
- queueMicrotask(() => {
684
- if (this.destroyed) {
685
- return;
686
- }
687
- this.render();
688
- });
689
- }
690
- }
691
- /**
692
- * Render the React component.
693
- */
694
- render() {
695
- var _a;
696
- if (this.destroyed) {
697
- return;
698
- }
699
- const Component = this.component;
700
- const props = this.props;
701
- const editor = this.editor;
702
- const isReact19 = isReact19Plus();
703
- const componentCanReceiveRef = canReceiveRef(Component);
704
- const elementProps = { ...props };
705
- if (elementProps.ref && !(isReact19 || componentCanReceiveRef)) {
706
- delete elementProps.ref;
707
- }
708
- if (!elementProps.ref && (isReact19 || componentCanReceiveRef)) {
709
- elementProps.ref = (ref) => {
710
- this.ref = ref;
711
- };
712
- }
713
- this.reactElement = /* @__PURE__ */ jsx5(Component, { ...elementProps });
714
- (_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.setRenderer(this.id, this);
715
- }
716
- /**
717
- * Re-renders the React component with new props.
718
- */
719
- updateProps(props = {}) {
720
- if (this.destroyed) {
721
- return;
722
- }
723
- this.props = {
724
- ...this.props,
725
- ...props
726
- };
727
- this.render();
728
- }
729
- /**
730
- * Destroy the React component.
731
- */
732
- destroy() {
733
- var _a;
734
- this.destroyed = true;
735
- const editor = this.editor;
736
- (_a = editor == null ? void 0 : editor.contentComponent) == null ? void 0 : _a.removeRenderer(this.id);
737
- try {
738
- if (this.element && this.element.parentNode) {
739
- this.element.parentNode.removeChild(this.element);
740
- }
741
- } catch {
742
- }
743
- }
744
- /**
745
- * Update the attributes of the element that holds the React component.
746
- */
747
- updateAttributes(attributes) {
748
- Object.keys(attributes).forEach((key) => {
749
- this.element.setAttribute(key, attributes[key]);
750
- });
751
- }
752
- };
753
-
754
- // src/ReactMarkViewRenderer.tsx
755
- import { jsx as jsx6 } from "react/jsx-runtime";
756
- var ReactMarkViewContext = React4.createContext({
757
- markViewContentRef: () => {
758
- }
759
- });
760
- var MarkViewContent = (props) => {
761
- const { as: Tag = "span", ...rest } = props;
762
- const { markViewContentRef } = React4.useContext(ReactMarkViewContext);
763
- return (
764
- // @ts-ignore
765
- /* @__PURE__ */ jsx6(Tag, { ...rest, ref: markViewContentRef, "data-mark-view-content": "" })
766
- );
767
- };
768
- var ReactMarkView = class extends MarkView {
769
- constructor(component, props, options) {
770
- super(component, props, options);
771
- const { as = "span", attrs, className = "" } = options || {};
772
- const componentProps = { ...props, updateAttributes: this.updateAttributes.bind(this) };
773
- this.contentDOMElement = document.createElement("span");
774
- const markViewContentRef = (el) => {
775
- if (el && !el.contains(this.contentDOMElement)) {
776
- el.appendChild(this.contentDOMElement);
777
- }
778
- };
779
- const context = {
780
- markViewContentRef
781
- };
782
- const ReactMarkViewProvider = React4.memo((componentProps2) => {
783
- return /* @__PURE__ */ jsx6(ReactMarkViewContext.Provider, { value: context, children: React4.createElement(component, componentProps2) });
784
- });
785
- ReactMarkViewProvider.displayName = "ReactMarkView";
786
- this.renderer = new ReactRenderer(ReactMarkViewProvider, {
787
- editor: props.editor,
788
- props: componentProps,
789
- as,
790
- className: `mark-${props.mark.type.name} ${className}`.trim()
791
- });
792
- if (attrs) {
793
- this.renderer.updateAttributes(attrs);
794
- }
795
- }
796
- get dom() {
797
- return this.renderer.element;
798
- }
799
- get contentDOM() {
800
- return this.contentDOMElement;
801
- }
802
- };
803
- function ReactMarkViewRenderer(component, options = {}) {
804
- return (props) => new ReactMarkView(component, props, options);
805
- }
806
-
807
- // src/ReactNodeViewRenderer.tsx
808
- import { getRenderedAttributes, NodeView } from "@tiptap/core";
809
- import { createElement as createElement2, createRef, memo } from "react";
810
- import { jsx as jsx7 } from "react/jsx-runtime";
811
- var ReactNodeView = class extends NodeView {
812
- constructor(component, props, options) {
813
- super(component, props, options);
814
- /**
815
- * The requestAnimationFrame ID used for selection updates.
816
- */
817
- this.selectionRafId = null;
818
- this.cachedExtensionWithSyncedStorage = null;
819
- if (!this.node.isLeaf) {
820
- if (this.options.contentDOMElementTag) {
821
- this.contentDOMElement = document.createElement(this.options.contentDOMElementTag);
822
- } else {
823
- this.contentDOMElement = document.createElement(this.node.isInline ? "span" : "div");
824
- }
825
- this.contentDOMElement.dataset.nodeViewContentReact = "";
826
- this.contentDOMElement.dataset.nodeViewWrapper = "";
827
- this.contentDOMElement.style.whiteSpace = "inherit";
828
- const contentTarget = this.dom.querySelector("[data-node-view-content]");
829
- if (!contentTarget) {
830
- return;
831
- }
832
- contentTarget.appendChild(this.contentDOMElement);
833
- }
834
- }
835
- /**
836
- * Returns a proxy of the extension that redirects storage access to the editor's mutable storage.
837
- * This preserves the original prototype chain (instanceof checks, methods like configure/extend work).
838
- * Cached to avoid proxy creation on every update.
839
- */
840
- get extensionWithSyncedStorage() {
841
- if (!this.cachedExtensionWithSyncedStorage) {
842
- const editor = this.editor;
843
- const extension = this.extension;
844
- this.cachedExtensionWithSyncedStorage = new Proxy(extension, {
845
- get(target, prop, receiver) {
846
- var _a;
847
- if (prop === "storage") {
848
- return (_a = editor.storage[extension.name]) != null ? _a : {};
849
- }
850
- return Reflect.get(target, prop, receiver);
851
- }
852
- });
853
- }
854
- return this.cachedExtensionWithSyncedStorage;
855
- }
856
- /**
857
- * Setup the React component.
858
- * Called on initialization.
859
- */
860
- mount() {
861
- const props = {
862
- editor: this.editor,
863
- node: this.node,
864
- decorations: this.decorations,
865
- innerDecorations: this.innerDecorations,
866
- view: this.view,
867
- selected: false,
868
- extension: this.extensionWithSyncedStorage,
869
- HTMLAttributes: this.HTMLAttributes,
870
- getPos: () => this.getPos(),
871
- updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
872
- deleteNode: () => this.deleteNode(),
873
- ref: createRef()
874
- };
875
- if (!this.component.displayName) {
876
- const capitalizeFirstChar = (string) => {
877
- return string.charAt(0).toUpperCase() + string.substring(1);
878
- };
879
- this.component.displayName = capitalizeFirstChar(this.extension.name);
880
- }
881
- const onDragStart = this.onDragStart.bind(this);
882
- const nodeViewContentRef = (element) => {
883
- if (element && this.contentDOMElement && element.firstChild !== this.contentDOMElement) {
884
- if (element.hasAttribute("data-node-view-wrapper")) {
885
- element.removeAttribute("data-node-view-wrapper");
886
- }
887
- element.appendChild(this.contentDOMElement);
888
- }
889
- };
890
- const context = { onDragStart, nodeViewContentRef };
891
- const Component = this.component;
892
- const ReactNodeViewProvider = memo((componentProps) => {
893
- return /* @__PURE__ */ jsx7(ReactNodeViewContext.Provider, { value: context, children: createElement2(Component, componentProps) });
894
- });
895
- ReactNodeViewProvider.displayName = "ReactNodeView";
896
- let as = this.node.isInline ? "span" : "div";
897
- if (this.options.as) {
898
- as = this.options.as;
899
- }
900
- const { className = "" } = this.options;
901
- this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this);
902
- this.renderer = new ReactRenderer(ReactNodeViewProvider, {
903
- editor: this.editor,
904
- props,
905
- as,
906
- className: `node-${this.node.type.name} ${className}`.trim()
907
- });
908
- this.editor.on("selectionUpdate", this.handleSelectionUpdate);
909
- this.updateElementAttributes();
910
- }
911
- /**
912
- * Return the DOM element.
913
- * This is the element that will be used to display the node view.
914
- */
915
- get dom() {
916
- var _a;
917
- if (this.renderer.element.firstElementChild && !((_a = this.renderer.element.firstElementChild) == null ? void 0 : _a.hasAttribute("data-node-view-wrapper"))) {
918
- throw Error("Please use the NodeViewWrapper component for your node view.");
919
- }
920
- return this.renderer.element;
921
- }
922
- /**
923
- * Return the content DOM element.
924
- * This is the element that will be used to display the rich-text content of the node.
925
- */
926
- get contentDOM() {
927
- if (this.node.isLeaf) {
928
- return null;
929
- }
930
- return this.contentDOMElement;
931
- }
932
- /**
933
- * On editor selection update, check if the node is selected.
934
- * If it is, call `selectNode`, otherwise call `deselectNode`.
935
- */
936
- handleSelectionUpdate() {
937
- if (this.selectionRafId) {
938
- cancelAnimationFrame(this.selectionRafId);
939
- this.selectionRafId = null;
940
- }
941
- this.selectionRafId = requestAnimationFrame(() => {
942
- this.selectionRafId = null;
943
- const { from, to } = this.editor.state.selection;
944
- const pos = this.getPos();
945
- if (typeof pos !== "number") {
946
- return;
947
- }
948
- if (from <= pos && to >= pos + this.node.nodeSize) {
949
- if (this.renderer.props.selected) {
950
- return;
951
- }
952
- this.selectNode();
953
- } else {
954
- if (!this.renderer.props.selected) {
955
- return;
956
- }
957
- this.deselectNode();
958
- }
959
- });
960
- }
961
- /**
962
- * On update, update the React component.
963
- * To prevent unnecessary updates, the `update` option can be used.
964
- */
965
- update(node, decorations, innerDecorations) {
966
- const rerenderComponent = (props) => {
967
- this.renderer.updateProps(props);
968
- if (typeof this.options.attrs === "function") {
969
- this.updateElementAttributes();
970
- }
971
- };
972
- if (node.type !== this.node.type) {
973
- return false;
974
- }
975
- if (typeof this.options.update === "function") {
976
- const oldNode = this.node;
977
- const oldDecorations = this.decorations;
978
- const oldInnerDecorations = this.innerDecorations;
979
- this.node = node;
980
- this.decorations = decorations;
981
- this.innerDecorations = innerDecorations;
982
- return this.options.update({
983
- oldNode,
984
- oldDecorations,
985
- newNode: node,
986
- newDecorations: decorations,
987
- oldInnerDecorations,
988
- innerDecorations,
989
- updateProps: () => rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage })
990
- });
991
- }
992
- if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
993
- return true;
994
- }
995
- this.node = node;
996
- this.decorations = decorations;
997
- this.innerDecorations = innerDecorations;
998
- rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage });
999
- return true;
1000
- }
1001
- /**
1002
- * Select the node.
1003
- * Add the `selected` prop and the `ProseMirror-selectednode` class.
1004
- */
1005
- selectNode() {
1006
- this.renderer.updateProps({
1007
- selected: true
1008
- });
1009
- this.renderer.element.classList.add("ProseMirror-selectednode");
1010
- }
1011
- /**
1012
- * Deselect the node.
1013
- * Remove the `selected` prop and the `ProseMirror-selectednode` class.
1014
- */
1015
- deselectNode() {
1016
- this.renderer.updateProps({
1017
- selected: false
1018
- });
1019
- this.renderer.element.classList.remove("ProseMirror-selectednode");
1020
- }
1021
- /**
1022
- * Destroy the React component instance.
1023
- */
1024
- destroy() {
1025
- this.renderer.destroy();
1026
- this.editor.off("selectionUpdate", this.handleSelectionUpdate);
1027
- this.contentDOMElement = null;
1028
- if (this.selectionRafId) {
1029
- cancelAnimationFrame(this.selectionRafId);
1030
- this.selectionRafId = null;
1031
- }
1032
- }
1033
- /**
1034
- * Update the attributes of the top-level element that holds the React component.
1035
- * Applying the attributes defined in the `attrs` option.
1036
- */
1037
- updateElementAttributes() {
1038
- if (this.options.attrs) {
1039
- let attrsObj = {};
1040
- if (typeof this.options.attrs === "function") {
1041
- const extensionAttributes = this.editor.extensionManager.attributes;
1042
- const HTMLAttributes = getRenderedAttributes(this.node, extensionAttributes);
1043
- attrsObj = this.options.attrs({ node: this.node, HTMLAttributes });
1044
- } else {
1045
- attrsObj = this.options.attrs;
1046
- }
1047
- this.renderer.updateAttributes(attrsObj);
1048
- }
1049
- }
1050
- };
1051
- function ReactNodeViewRenderer(component, options) {
1052
- return (props) => {
1053
- if (!props.editor.contentComponent) {
1054
- return {};
1055
- }
1056
- return new ReactNodeView(component, props, options);
1057
- };
1058
- }
1059
-
1060
- // src/Tiptap.tsx
1061
- import { createContext as createContext3, useContext as useContext3, useMemo as useMemo2 } from "react";
1062
- import { jsx as jsx8 } from "react/jsx-runtime";
1063
- var TiptapContext = createContext3({
1064
- get editor() {
1065
- throw new Error("useTiptap must be used within a <Tiptap> provider");
1066
- }
1067
- });
1068
- TiptapContext.displayName = "TiptapContext";
1069
- var useTiptap = () => useContext3(TiptapContext);
1070
- function useTiptapState(selector, equalityFn) {
1071
- const { editor } = useTiptap();
1072
- return useEditorState({
1073
- editor,
1074
- selector,
1075
- equalityFn
1076
- });
1077
- }
1078
- function TiptapWrapper({ editor, instance, children }) {
1079
- const resolvedEditor = editor != null ? editor : instance;
1080
- if (!resolvedEditor) {
1081
- throw new Error("Tiptap: An editor instance is required. Pass a non-null `editor` prop.");
1082
- }
1083
- const tiptapContextValue = useMemo2(() => ({ editor: resolvedEditor }), [resolvedEditor]);
1084
- const legacyContextValue = useMemo2(() => ({ editor: resolvedEditor }), [resolvedEditor]);
1085
- return /* @__PURE__ */ jsx8(EditorContext.Provider, { value: legacyContextValue, children: /* @__PURE__ */ jsx8(TiptapContext.Provider, { value: tiptapContextValue, children }) });
1086
- }
1087
- TiptapWrapper.displayName = "Tiptap";
1088
- function TiptapContent({ ...rest }) {
1089
- const { editor } = useTiptap();
1090
- return /* @__PURE__ */ jsx8(EditorContent, { editor, ...rest });
1091
- }
1092
- TiptapContent.displayName = "Tiptap.Content";
1093
- var Tiptap = Object.assign(TiptapWrapper, {
1094
- /**
1095
- * The Tiptap Content component that renders the EditorContent with the editor instance from the context.
1096
- * @see TiptapContent
1097
- */
1098
- Content: TiptapContent
1099
- });
1100
-
1101
- // src/index.ts
1102
- export * from "@tiptap/core";
1103
- export {
1104
- EditorConsumer,
1105
- EditorContent,
1106
- EditorContext,
1107
- EditorProvider,
1108
- MarkViewContent,
1109
- NodeViewContent,
1110
- NodeViewWrapper,
1111
- PureEditorContent,
1112
- ReactMarkView,
1113
- ReactMarkViewContext,
1114
- ReactMarkViewRenderer,
1115
- ReactNodeView,
1116
- ReactNodeViewContentProvider,
1117
- ReactNodeViewContext,
1118
- ReactNodeViewRenderer,
1119
- ReactRenderer,
1120
- Tiptap,
1121
- TiptapContent,
1122
- TiptapContext,
1123
- TiptapWrapper,
1124
- useCurrentEditor,
1125
- useEditor,
1126
- useEditorState,
1127
- useReactNodeView,
1128
- useTiptap,
1129
- useTiptapState
1130
- };
1131
- //# sourceMappingURL=index.js.map