@tiptap/vue-3 3.20.3 → 3.20.4

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 ADDED
@@ -0,0 +1,655 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ Editor: () => Editor,
25
+ EditorContent: () => EditorContent,
26
+ MarkViewContent: () => MarkViewContent,
27
+ NodeViewContent: () => NodeViewContent,
28
+ NodeViewWrapper: () => NodeViewWrapper,
29
+ VueMarkView: () => VueMarkView,
30
+ VueMarkViewRenderer: () => VueMarkViewRenderer,
31
+ VueNodeViewRenderer: () => VueNodeViewRenderer,
32
+ VueRenderer: () => VueRenderer,
33
+ markViewProps: () => markViewProps,
34
+ nodeViewProps: () => nodeViewProps,
35
+ useEditor: () => useEditor
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/Editor.ts
40
+ var import_core = require("@tiptap/core");
41
+ var import_vue = require("vue");
42
+ function useDebouncedRef(value) {
43
+ return (0, import_vue.customRef)((track, trigger) => {
44
+ return {
45
+ get() {
46
+ track();
47
+ return value;
48
+ },
49
+ set(newValue) {
50
+ value = newValue;
51
+ requestAnimationFrame(() => {
52
+ requestAnimationFrame(() => {
53
+ trigger();
54
+ });
55
+ });
56
+ }
57
+ };
58
+ });
59
+ }
60
+ var Editor = class extends import_core.Editor {
61
+ constructor(options = {}) {
62
+ super(options);
63
+ this.contentComponent = null;
64
+ this.appContext = null;
65
+ this.reactiveState = useDebouncedRef(this.view.state);
66
+ this.reactiveExtensionStorage = useDebouncedRef(this.extensionStorage);
67
+ this.on("beforeTransaction", ({ nextState }) => {
68
+ this.reactiveState.value = nextState;
69
+ this.reactiveExtensionStorage.value = this.extensionStorage;
70
+ });
71
+ return (0, import_vue.markRaw)(this);
72
+ }
73
+ get state() {
74
+ return this.reactiveState ? this.reactiveState.value : this.view.state;
75
+ }
76
+ get storage() {
77
+ return this.reactiveExtensionStorage ? this.reactiveExtensionStorage.value : super.storage;
78
+ }
79
+ /**
80
+ * Register a ProseMirror plugin.
81
+ */
82
+ registerPlugin(plugin, handlePlugins) {
83
+ const nextState = super.registerPlugin(plugin, handlePlugins);
84
+ if (this.reactiveState) {
85
+ this.reactiveState.value = nextState;
86
+ }
87
+ return nextState;
88
+ }
89
+ /**
90
+ * Unregister a ProseMirror plugin.
91
+ */
92
+ unregisterPlugin(nameOrPluginKey) {
93
+ const nextState = super.unregisterPlugin(nameOrPluginKey);
94
+ if (this.reactiveState && nextState) {
95
+ this.reactiveState.value = nextState;
96
+ }
97
+ return nextState;
98
+ }
99
+ };
100
+
101
+ // src/EditorContent.ts
102
+ var import_vue2 = require("vue");
103
+ var EditorContent = (0, import_vue2.defineComponent)({
104
+ name: "EditorContent",
105
+ props: {
106
+ editor: {
107
+ default: null,
108
+ type: Object
109
+ }
110
+ },
111
+ setup(props) {
112
+ const rootEl = (0, import_vue2.ref)();
113
+ const instance = (0, import_vue2.getCurrentInstance)();
114
+ (0, import_vue2.watchEffect)(() => {
115
+ const editor = props.editor;
116
+ if (editor && editor.options.element && rootEl.value) {
117
+ (0, import_vue2.nextTick)(() => {
118
+ var _a;
119
+ if (!rootEl.value || !((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) {
120
+ return;
121
+ }
122
+ const element = (0, import_vue2.unref)(rootEl.value);
123
+ rootEl.value.append(...editor.view.dom.parentNode.childNodes);
124
+ editor.contentComponent = instance.ctx._;
125
+ if (instance) {
126
+ editor.appContext = {
127
+ ...instance.appContext,
128
+ // Vue internally uses prototype chain to forward/shadow injects across the entire component chain
129
+ // so don't use object spread operator or 'Object.assign' and just set `provides` as is on editor's appContext
130
+ // @ts-expect-error forward instance's 'provides' into appContext
131
+ provides: instance.provides
132
+ };
133
+ }
134
+ editor.setOptions({
135
+ element
136
+ });
137
+ editor.createNodeViews();
138
+ });
139
+ }
140
+ });
141
+ (0, import_vue2.onBeforeUnmount)(() => {
142
+ const editor = props.editor;
143
+ if (!editor) {
144
+ return;
145
+ }
146
+ editor.contentComponent = null;
147
+ editor.appContext = null;
148
+ });
149
+ return { rootEl };
150
+ },
151
+ render() {
152
+ return (0, import_vue2.h)("div", {
153
+ ref: (el) => {
154
+ this.rootEl = el;
155
+ }
156
+ });
157
+ }
158
+ });
159
+
160
+ // src/NodeViewContent.ts
161
+ var import_vue3 = require("vue");
162
+ var NodeViewContent = (0, import_vue3.defineComponent)({
163
+ name: "NodeViewContent",
164
+ props: {
165
+ as: {
166
+ type: String,
167
+ default: "div"
168
+ }
169
+ },
170
+ render() {
171
+ return (0, import_vue3.h)(this.as, {
172
+ style: {
173
+ whiteSpace: "pre-wrap"
174
+ },
175
+ "data-node-view-content": ""
176
+ });
177
+ }
178
+ });
179
+
180
+ // src/NodeViewWrapper.ts
181
+ var import_vue4 = require("vue");
182
+ var NodeViewWrapper = (0, import_vue4.defineComponent)({
183
+ name: "NodeViewWrapper",
184
+ props: {
185
+ as: {
186
+ type: String,
187
+ default: "div"
188
+ }
189
+ },
190
+ inject: ["onDragStart", "decorationClasses"],
191
+ render() {
192
+ var _a, _b;
193
+ return (0, import_vue4.h)(
194
+ this.as,
195
+ {
196
+ // @ts-ignore
197
+ class: this.decorationClasses,
198
+ style: {
199
+ whiteSpace: "normal"
200
+ },
201
+ "data-node-view-wrapper": "",
202
+ // @ts-ignore (https://github.com/vuejs/vue-next/issues/3031)
203
+ onDragstart: this.onDragStart
204
+ },
205
+ (_b = (_a = this.$slots).default) == null ? void 0 : _b.call(_a)
206
+ );
207
+ }
208
+ });
209
+
210
+ // src/useEditor.ts
211
+ var import_vue5 = require("vue");
212
+ var useEditor = (options = {}) => {
213
+ const editor = (0, import_vue5.shallowRef)();
214
+ (0, import_vue5.onMounted)(() => {
215
+ editor.value = new Editor(options);
216
+ });
217
+ (0, import_vue5.onBeforeUnmount)(() => {
218
+ var _a, _b, _c, _d;
219
+ const nodes = (_b = (_a = editor.value) == null ? void 0 : _a.view.dom) == null ? void 0 : _b.parentNode;
220
+ const newEl = nodes == null ? void 0 : nodes.cloneNode(true);
221
+ (_c = nodes == null ? void 0 : nodes.parentNode) == null ? void 0 : _c.replaceChild(newEl, nodes);
222
+ (_d = editor.value) == null ? void 0 : _d.destroy();
223
+ });
224
+ return editor;
225
+ };
226
+
227
+ // src/VueMarkViewRenderer.ts
228
+ var import_core2 = require("@tiptap/core");
229
+ var import_vue7 = require("vue");
230
+
231
+ // src/VueRenderer.ts
232
+ var import_vue6 = require("vue");
233
+ var VueRenderer = class {
234
+ constructor(component, { props = {}, editor }) {
235
+ /**
236
+ * Flag to track if the renderer has been destroyed, preventing queued or asynchronous renders from executing after teardown.
237
+ */
238
+ this.destroyed = false;
239
+ this.editor = editor;
240
+ this.component = (0, import_vue6.markRaw)(component);
241
+ this.el = document.createElement("div");
242
+ this.props = (0, import_vue6.reactive)(props);
243
+ this.renderedComponent = this.renderComponent();
244
+ }
245
+ get element() {
246
+ return this.renderedComponent.el;
247
+ }
248
+ get ref() {
249
+ var _a, _b, _c, _d;
250
+ if ((_b = (_a = this.renderedComponent.vNode) == null ? void 0 : _a.component) == null ? void 0 : _b.exposed) {
251
+ return this.renderedComponent.vNode.component.exposed;
252
+ }
253
+ return (_d = (_c = this.renderedComponent.vNode) == null ? void 0 : _c.component) == null ? void 0 : _d.proxy;
254
+ }
255
+ renderComponent() {
256
+ if (this.destroyed) {
257
+ return this.renderedComponent;
258
+ }
259
+ let vNode = (0, import_vue6.h)(this.component, this.props);
260
+ if (this.editor.appContext) {
261
+ vNode.appContext = this.editor.appContext;
262
+ }
263
+ if (typeof document !== "undefined" && this.el) {
264
+ (0, import_vue6.render)(vNode, this.el);
265
+ }
266
+ const destroy = () => {
267
+ if (this.el) {
268
+ (0, import_vue6.render)(null, this.el);
269
+ }
270
+ this.el = null;
271
+ vNode = null;
272
+ };
273
+ return { vNode, destroy, el: this.el ? this.el.firstElementChild : null };
274
+ }
275
+ updateProps(props = {}) {
276
+ if (this.destroyed) {
277
+ return;
278
+ }
279
+ Object.entries(props).forEach(([key, value]) => {
280
+ this.props[key] = value;
281
+ });
282
+ this.renderComponent();
283
+ }
284
+ destroy() {
285
+ if (this.destroyed) {
286
+ return;
287
+ }
288
+ this.destroyed = true;
289
+ this.renderedComponent.destroy();
290
+ }
291
+ };
292
+
293
+ // src/VueMarkViewRenderer.ts
294
+ var markViewProps = {
295
+ editor: {
296
+ type: Object,
297
+ required: true
298
+ },
299
+ mark: {
300
+ type: Object,
301
+ required: true
302
+ },
303
+ extension: {
304
+ type: Object,
305
+ required: true
306
+ },
307
+ inline: {
308
+ type: Boolean,
309
+ required: true
310
+ },
311
+ view: {
312
+ type: Object,
313
+ required: true
314
+ },
315
+ updateAttributes: {
316
+ type: Function,
317
+ required: true
318
+ },
319
+ HTMLAttributes: {
320
+ type: Object,
321
+ required: true
322
+ }
323
+ };
324
+ var MarkViewContent = (0, import_vue7.defineComponent)({
325
+ name: "MarkViewContent",
326
+ props: {
327
+ as: {
328
+ type: String,
329
+ default: "span"
330
+ }
331
+ },
332
+ render() {
333
+ return (0, import_vue7.h)(this.as, {
334
+ style: {
335
+ whiteSpace: "inherit"
336
+ },
337
+ "data-mark-view-content": ""
338
+ });
339
+ }
340
+ });
341
+ var VueMarkView = class extends import_core2.MarkView {
342
+ constructor(component, props, options) {
343
+ super(component, props, options);
344
+ const componentProps = { ...props, updateAttributes: this.updateAttributes.bind(this) };
345
+ const extendedComponent = (0, import_vue7.defineComponent)({
346
+ extends: { ...component },
347
+ props: Object.keys(componentProps),
348
+ template: this.component.template,
349
+ setup: (reactiveProps) => {
350
+ var _a;
351
+ return (_a = component.setup) == null ? void 0 : _a.call(component, reactiveProps, {
352
+ expose: () => void 0
353
+ });
354
+ },
355
+ // Add support for scoped styles
356
+ __scopeId: component.__scopeId,
357
+ __cssModules: component.__cssModules,
358
+ __name: component.__name,
359
+ __file: component.__file
360
+ });
361
+ this.renderer = new VueRenderer(extendedComponent, {
362
+ editor: this.editor,
363
+ props: componentProps
364
+ });
365
+ }
366
+ get dom() {
367
+ return this.renderer.element;
368
+ }
369
+ get contentDOM() {
370
+ return this.dom.querySelector("[data-mark-view-content]");
371
+ }
372
+ updateAttributes(attrs) {
373
+ const unproxiedMark = (0, import_vue7.toRaw)(this.mark);
374
+ super.updateAttributes(attrs, unproxiedMark);
375
+ }
376
+ destroy() {
377
+ this.renderer.destroy();
378
+ }
379
+ };
380
+ function VueMarkViewRenderer(component, options = {}) {
381
+ return (props) => {
382
+ if (!props.editor.contentComponent) {
383
+ return {};
384
+ }
385
+ return new VueMarkView(component, props, options);
386
+ };
387
+ }
388
+
389
+ // src/VueNodeViewRenderer.ts
390
+ var import_core3 = require("@tiptap/core");
391
+ var import_vue8 = require("vue");
392
+ var nodeViewProps = {
393
+ editor: {
394
+ type: Object,
395
+ required: true
396
+ },
397
+ node: {
398
+ type: Object,
399
+ required: true
400
+ },
401
+ decorations: {
402
+ type: Object,
403
+ required: true
404
+ },
405
+ selected: {
406
+ type: Boolean,
407
+ required: true
408
+ },
409
+ extension: {
410
+ type: Object,
411
+ required: true
412
+ },
413
+ getPos: {
414
+ type: Function,
415
+ required: true
416
+ },
417
+ updateAttributes: {
418
+ type: Function,
419
+ required: true
420
+ },
421
+ deleteNode: {
422
+ type: Function,
423
+ required: true
424
+ },
425
+ view: {
426
+ type: Object,
427
+ required: true
428
+ },
429
+ innerDecorations: {
430
+ type: Object,
431
+ required: true
432
+ },
433
+ HTMLAttributes: {
434
+ type: Object,
435
+ required: true
436
+ }
437
+ };
438
+ var VueNodeView = class extends import_core3.NodeView {
439
+ constructor() {
440
+ super(...arguments);
441
+ this.cachedExtensionWithSyncedStorage = null;
442
+ }
443
+ /**
444
+ * Returns a proxy of the extension that redirects storage access to the editor's mutable storage.
445
+ * This preserves the original prototype chain (instanceof checks, methods like configure/extend work).
446
+ * Cached to avoid proxy creation on every update.
447
+ */
448
+ get extensionWithSyncedStorage() {
449
+ if (!this.cachedExtensionWithSyncedStorage) {
450
+ const editor = this.editor;
451
+ const extension = this.extension;
452
+ this.cachedExtensionWithSyncedStorage = new Proxy(extension, {
453
+ get(target, prop, receiver) {
454
+ var _a;
455
+ if (prop === "storage") {
456
+ return (_a = editor.storage[extension.name]) != null ? _a : {};
457
+ }
458
+ return Reflect.get(target, prop, receiver);
459
+ }
460
+ });
461
+ }
462
+ return this.cachedExtensionWithSyncedStorage;
463
+ }
464
+ mount() {
465
+ const props = {
466
+ editor: this.editor,
467
+ node: this.node,
468
+ decorations: this.decorations,
469
+ innerDecorations: this.innerDecorations,
470
+ view: this.view,
471
+ selected: false,
472
+ extension: this.extensionWithSyncedStorage,
473
+ HTMLAttributes: this.HTMLAttributes,
474
+ getPos: () => this.getPos(),
475
+ updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
476
+ deleteNode: () => this.deleteNode()
477
+ };
478
+ const onDragStart = this.onDragStart.bind(this);
479
+ this.decorationClasses = (0, import_vue8.ref)(this.getDecorationClasses());
480
+ const extendedComponent = (0, import_vue8.defineComponent)({
481
+ extends: { ...this.component },
482
+ props: Object.keys(props),
483
+ template: this.component.template,
484
+ setup: (reactiveProps) => {
485
+ var _a, _b;
486
+ (0, import_vue8.provide)("onDragStart", onDragStart);
487
+ (0, import_vue8.provide)("decorationClasses", this.decorationClasses);
488
+ return (_b = (_a = this.component).setup) == null ? void 0 : _b.call(_a, reactiveProps, {
489
+ expose: () => void 0
490
+ });
491
+ },
492
+ // add support for scoped styles
493
+ // @ts-ignore
494
+ // eslint-disable-next-line
495
+ __scopeId: this.component.__scopeId,
496
+ // add support for CSS Modules
497
+ // @ts-ignore
498
+ // eslint-disable-next-line
499
+ __cssModules: this.component.__cssModules,
500
+ // add support for vue devtools
501
+ // @ts-ignore
502
+ // eslint-disable-next-line
503
+ __name: this.component.__name,
504
+ // @ts-ignore
505
+ // eslint-disable-next-line
506
+ __file: this.component.__file
507
+ });
508
+ this.handleSelectionUpdate = this.handleSelectionUpdate.bind(this);
509
+ this.editor.on("selectionUpdate", this.handleSelectionUpdate);
510
+ this.renderer = new VueRenderer(extendedComponent, {
511
+ editor: this.editor,
512
+ props
513
+ });
514
+ }
515
+ /**
516
+ * Return the DOM element.
517
+ * This is the element that will be used to display the node view.
518
+ */
519
+ get dom() {
520
+ if (!this.renderer.element || !this.renderer.element.hasAttribute("data-node-view-wrapper")) {
521
+ throw Error("Please use the NodeViewWrapper component for your node view.");
522
+ }
523
+ return this.renderer.element;
524
+ }
525
+ /**
526
+ * Return the content DOM element.
527
+ * This is the element that will be used to display the rich-text content of the node.
528
+ */
529
+ get contentDOM() {
530
+ if (this.node.isLeaf) {
531
+ return null;
532
+ }
533
+ return this.dom.querySelector("[data-node-view-content]");
534
+ }
535
+ /**
536
+ * On editor selection update, check if the node is selected.
537
+ * If it is, call `selectNode`, otherwise call `deselectNode`.
538
+ */
539
+ handleSelectionUpdate() {
540
+ const { from, to } = this.editor.state.selection;
541
+ const pos = this.getPos();
542
+ if (typeof pos !== "number") {
543
+ return;
544
+ }
545
+ if (from <= pos && to >= pos + this.node.nodeSize) {
546
+ if (this.renderer.props.selected) {
547
+ return;
548
+ }
549
+ this.selectNode();
550
+ } else {
551
+ if (!this.renderer.props.selected) {
552
+ return;
553
+ }
554
+ this.deselectNode();
555
+ }
556
+ }
557
+ /**
558
+ * On update, update the React component.
559
+ * To prevent unnecessary updates, the `update` option can be used.
560
+ */
561
+ update(node, decorations, innerDecorations) {
562
+ const rerenderComponent = (props) => {
563
+ this.decorationClasses.value = this.getDecorationClasses();
564
+ this.renderer.updateProps(props);
565
+ };
566
+ if (typeof this.options.update === "function") {
567
+ const oldNode = this.node;
568
+ const oldDecorations = this.decorations;
569
+ const oldInnerDecorations = this.innerDecorations;
570
+ this.node = node;
571
+ this.decorations = decorations;
572
+ this.innerDecorations = innerDecorations;
573
+ return this.options.update({
574
+ oldNode,
575
+ oldDecorations,
576
+ newNode: node,
577
+ newDecorations: decorations,
578
+ oldInnerDecorations,
579
+ innerDecorations,
580
+ updateProps: () => rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage })
581
+ });
582
+ }
583
+ if (node.type !== this.node.type) {
584
+ return false;
585
+ }
586
+ if (node === this.node && this.decorations === decorations && this.innerDecorations === innerDecorations) {
587
+ return true;
588
+ }
589
+ this.node = node;
590
+ this.decorations = decorations;
591
+ this.innerDecorations = innerDecorations;
592
+ rerenderComponent({ node, decorations, innerDecorations, extension: this.extensionWithSyncedStorage });
593
+ return true;
594
+ }
595
+ /**
596
+ * Select the node.
597
+ * Add the `selected` prop and the `ProseMirror-selectednode` class.
598
+ */
599
+ selectNode() {
600
+ this.renderer.updateProps({
601
+ selected: true
602
+ });
603
+ if (this.renderer.element) {
604
+ this.renderer.element.classList.add("ProseMirror-selectednode");
605
+ }
606
+ }
607
+ /**
608
+ * Deselect the node.
609
+ * Remove the `selected` prop and the `ProseMirror-selectednode` class.
610
+ */
611
+ deselectNode() {
612
+ this.renderer.updateProps({
613
+ selected: false
614
+ });
615
+ if (this.renderer.element) {
616
+ this.renderer.element.classList.remove("ProseMirror-selectednode");
617
+ }
618
+ }
619
+ getDecorationClasses() {
620
+ return this.decorations.flatMap((item) => item.type.attrs.class).join(" ");
621
+ }
622
+ destroy() {
623
+ this.renderer.destroy();
624
+ this.editor.off("selectionUpdate", this.handleSelectionUpdate);
625
+ }
626
+ };
627
+ function VueNodeViewRenderer(component, options) {
628
+ return (props) => {
629
+ if (!props.editor.contentComponent) {
630
+ return {};
631
+ }
632
+ const normalizedComponent = typeof component === "function" && "__vccOpts" in component ? component.__vccOpts : component;
633
+ return new VueNodeView(normalizedComponent, props, options);
634
+ };
635
+ }
636
+
637
+ // src/index.ts
638
+ __reExport(index_exports, require("@tiptap/core"), module.exports);
639
+ // Annotate the CommonJS export names for ESM import in node:
640
+ 0 && (module.exports = {
641
+ Editor,
642
+ EditorContent,
643
+ MarkViewContent,
644
+ NodeViewContent,
645
+ NodeViewWrapper,
646
+ VueMarkView,
647
+ VueMarkViewRenderer,
648
+ VueNodeViewRenderer,
649
+ VueRenderer,
650
+ markViewProps,
651
+ nodeViewProps,
652
+ useEditor,
653
+ ...require("@tiptap/core")
654
+ });
655
+ //# sourceMappingURL=index.cjs.map