@tiptap/vue-3 2.0.0-beta.21 → 2.0.0-beta.211

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 ADDED
@@ -0,0 +1,523 @@
1
+ // src/BubbleMenu.ts
2
+ import { BubbleMenuPlugin } from "@tiptap/extension-bubble-menu";
3
+ import {
4
+ defineComponent,
5
+ h,
6
+ onBeforeUnmount,
7
+ onMounted,
8
+ ref
9
+ } from "vue";
10
+ var BubbleMenu = defineComponent({
11
+ name: "BubbleMenu",
12
+ props: {
13
+ pluginKey: {
14
+ type: null,
15
+ default: "bubbleMenu"
16
+ },
17
+ editor: {
18
+ type: Object,
19
+ required: true
20
+ },
21
+ updateDelay: {
22
+ type: Number,
23
+ default: void 0
24
+ },
25
+ tippyOptions: {
26
+ type: Object,
27
+ default: () => ({})
28
+ },
29
+ shouldShow: {
30
+ type: Function,
31
+ default: null
32
+ }
33
+ },
34
+ setup(props, { slots }) {
35
+ const root = ref(null);
36
+ onMounted(() => {
37
+ const {
38
+ updateDelay,
39
+ editor,
40
+ pluginKey,
41
+ shouldShow,
42
+ tippyOptions
43
+ } = props;
44
+ editor.registerPlugin(BubbleMenuPlugin({
45
+ updateDelay,
46
+ editor,
47
+ element: root.value,
48
+ pluginKey,
49
+ shouldShow,
50
+ tippyOptions
51
+ }));
52
+ });
53
+ onBeforeUnmount(() => {
54
+ const { pluginKey, editor } = props;
55
+ editor.unregisterPlugin(pluginKey);
56
+ });
57
+ return () => {
58
+ var _a;
59
+ return h("div", { ref: root }, (_a = slots.default) == null ? void 0 : _a.call(slots));
60
+ };
61
+ }
62
+ });
63
+
64
+ // src/Editor.ts
65
+ import { Editor as CoreEditor } from "@tiptap/core";
66
+ import {
67
+ customRef,
68
+ markRaw,
69
+ reactive
70
+ } from "vue";
71
+ function useDebouncedRef(value) {
72
+ return customRef((track, trigger) => {
73
+ return {
74
+ get() {
75
+ track();
76
+ return value;
77
+ },
78
+ set(newValue) {
79
+ value = newValue;
80
+ requestAnimationFrame(() => {
81
+ requestAnimationFrame(() => {
82
+ trigger();
83
+ });
84
+ });
85
+ }
86
+ };
87
+ });
88
+ }
89
+ var Editor = class extends CoreEditor {
90
+ constructor(options = {}) {
91
+ super(options);
92
+ this.vueRenderers = reactive(/* @__PURE__ */ new Map());
93
+ this.contentComponent = null;
94
+ this.reactiveState = useDebouncedRef(this.view.state);
95
+ this.reactiveExtensionStorage = useDebouncedRef(this.extensionStorage);
96
+ this.on("transaction", () => {
97
+ this.reactiveState.value = this.view.state;
98
+ this.reactiveExtensionStorage.value = this.extensionStorage;
99
+ });
100
+ return markRaw(this);
101
+ }
102
+ get state() {
103
+ return this.reactiveState ? this.reactiveState.value : this.view.state;
104
+ }
105
+ get storage() {
106
+ return this.reactiveExtensionStorage ? this.reactiveExtensionStorage.value : super.storage;
107
+ }
108
+ registerPlugin(plugin, handlePlugins) {
109
+ super.registerPlugin(plugin, handlePlugins);
110
+ this.reactiveState.value = this.view.state;
111
+ }
112
+ unregisterPlugin(nameOrPluginKey) {
113
+ super.unregisterPlugin(nameOrPluginKey);
114
+ this.reactiveState.value = this.view.state;
115
+ }
116
+ };
117
+
118
+ // src/EditorContent.ts
119
+ import {
120
+ defineComponent as defineComponent2,
121
+ getCurrentInstance,
122
+ h as h2,
123
+ nextTick,
124
+ onBeforeUnmount as onBeforeUnmount2,
125
+ ref as ref2,
126
+ Teleport,
127
+ unref,
128
+ watchEffect
129
+ } from "vue";
130
+ var EditorContent = defineComponent2({
131
+ name: "EditorContent",
132
+ props: {
133
+ editor: {
134
+ default: null,
135
+ type: Object
136
+ }
137
+ },
138
+ setup(props) {
139
+ const rootEl = ref2();
140
+ const instance = getCurrentInstance();
141
+ watchEffect(() => {
142
+ const editor = props.editor;
143
+ if (editor && editor.options.element && rootEl.value) {
144
+ nextTick(() => {
145
+ if (!rootEl.value || !editor.options.element.firstChild) {
146
+ return;
147
+ }
148
+ const element = unref(rootEl.value);
149
+ rootEl.value.append(...editor.options.element.childNodes);
150
+ editor.contentComponent = instance.ctx._;
151
+ editor.setOptions({
152
+ element
153
+ });
154
+ editor.createNodeViews();
155
+ });
156
+ }
157
+ });
158
+ onBeforeUnmount2(() => {
159
+ const editor = props.editor;
160
+ if (!editor) {
161
+ return;
162
+ }
163
+ if (!editor.isDestroyed) {
164
+ editor.view.setProps({
165
+ nodeViews: {}
166
+ });
167
+ }
168
+ editor.contentComponent = null;
169
+ if (!editor.options.element.firstChild) {
170
+ return;
171
+ }
172
+ const newElement = document.createElement("div");
173
+ newElement.append(...editor.options.element.childNodes);
174
+ editor.setOptions({
175
+ element: newElement
176
+ });
177
+ });
178
+ return { rootEl };
179
+ },
180
+ render() {
181
+ const vueRenderers = [];
182
+ if (this.editor) {
183
+ this.editor.vueRenderers.forEach((vueRenderer) => {
184
+ const node = h2(
185
+ Teleport,
186
+ {
187
+ to: vueRenderer.teleportElement,
188
+ key: vueRenderer.id
189
+ },
190
+ h2(
191
+ vueRenderer.component,
192
+ {
193
+ ref: vueRenderer.id,
194
+ ...vueRenderer.props
195
+ }
196
+ )
197
+ );
198
+ vueRenderers.push(node);
199
+ });
200
+ }
201
+ return h2(
202
+ "div",
203
+ {
204
+ ref: (el) => {
205
+ this.rootEl = el;
206
+ }
207
+ },
208
+ ...vueRenderers
209
+ );
210
+ }
211
+ });
212
+
213
+ // src/FloatingMenu.ts
214
+ import { FloatingMenuPlugin } from "@tiptap/extension-floating-menu";
215
+ import {
216
+ defineComponent as defineComponent3,
217
+ h as h3,
218
+ onBeforeUnmount as onBeforeUnmount3,
219
+ onMounted as onMounted2,
220
+ ref as ref3
221
+ } from "vue";
222
+ var FloatingMenu = defineComponent3({
223
+ name: "FloatingMenu",
224
+ props: {
225
+ pluginKey: {
226
+ type: null,
227
+ default: "floatingMenu"
228
+ },
229
+ editor: {
230
+ type: Object,
231
+ required: true
232
+ },
233
+ tippyOptions: {
234
+ type: Object,
235
+ default: () => ({})
236
+ },
237
+ shouldShow: {
238
+ type: Function,
239
+ default: null
240
+ }
241
+ },
242
+ setup(props, { slots }) {
243
+ const root = ref3(null);
244
+ onMounted2(() => {
245
+ const {
246
+ pluginKey,
247
+ editor,
248
+ tippyOptions,
249
+ shouldShow
250
+ } = props;
251
+ editor.registerPlugin(FloatingMenuPlugin({
252
+ pluginKey,
253
+ editor,
254
+ element: root.value,
255
+ tippyOptions,
256
+ shouldShow
257
+ }));
258
+ });
259
+ onBeforeUnmount3(() => {
260
+ const { pluginKey, editor } = props;
261
+ editor.unregisterPlugin(pluginKey);
262
+ });
263
+ return () => {
264
+ var _a;
265
+ return h3("div", { ref: root }, (_a = slots.default) == null ? void 0 : _a.call(slots));
266
+ };
267
+ }
268
+ });
269
+
270
+ // src/NodeViewContent.ts
271
+ import { defineComponent as defineComponent4, h as h4 } from "vue";
272
+ var NodeViewContent = defineComponent4({
273
+ props: {
274
+ as: {
275
+ type: String,
276
+ default: "div"
277
+ }
278
+ },
279
+ render() {
280
+ return h4(this.as, {
281
+ style: {
282
+ whiteSpace: "pre-wrap"
283
+ },
284
+ "data-node-view-content": ""
285
+ });
286
+ }
287
+ });
288
+
289
+ // src/NodeViewWrapper.ts
290
+ import { defineComponent as defineComponent5, h as h5 } from "vue";
291
+ var NodeViewWrapper = defineComponent5({
292
+ props: {
293
+ as: {
294
+ type: String,
295
+ default: "div"
296
+ }
297
+ },
298
+ inject: ["onDragStart", "decorationClasses"],
299
+ render() {
300
+ var _a, _b;
301
+ return h5(
302
+ this.as,
303
+ {
304
+ class: this.decorationClasses,
305
+ style: {
306
+ whiteSpace: "normal"
307
+ },
308
+ "data-node-view-wrapper": "",
309
+ onDragstart: this.onDragStart
310
+ },
311
+ (_b = (_a = this.$slots).default) == null ? void 0 : _b.call(_a)
312
+ );
313
+ }
314
+ });
315
+
316
+ // src/useEditor.ts
317
+ import { onBeforeUnmount as onBeforeUnmount4, onMounted as onMounted3, shallowRef } from "vue";
318
+ var useEditor = (options = {}) => {
319
+ const editor = shallowRef();
320
+ onMounted3(() => {
321
+ editor.value = new Editor(options);
322
+ });
323
+ onBeforeUnmount4(() => {
324
+ var _a;
325
+ (_a = editor.value) == null ? void 0 : _a.destroy();
326
+ });
327
+ return editor;
328
+ };
329
+
330
+ // src/VueNodeViewRenderer.ts
331
+ import {
332
+ NodeView
333
+ } from "@tiptap/core";
334
+ import {
335
+ defineComponent as defineComponent6,
336
+ provide,
337
+ ref as ref4
338
+ } from "vue";
339
+
340
+ // src/VueRenderer.ts
341
+ import { markRaw as markRaw2, reactive as reactive2 } from "vue";
342
+ var VueRenderer = class {
343
+ constructor(component, { props = {}, editor }) {
344
+ this.id = Math.floor(Math.random() * 4294967295).toString();
345
+ this.editor = editor;
346
+ this.component = markRaw2(component);
347
+ this.teleportElement = document.createElement("div");
348
+ this.element = this.teleportElement;
349
+ this.props = reactive2(props);
350
+ this.editor.vueRenderers.set(this.id, this);
351
+ if (this.editor.contentComponent) {
352
+ this.editor.contentComponent.update();
353
+ if (this.teleportElement.children.length !== 1) {
354
+ throw Error("VueRenderer doesn\u2019t support multiple child elements.");
355
+ }
356
+ this.element = this.teleportElement.firstElementChild;
357
+ }
358
+ }
359
+ get ref() {
360
+ var _a;
361
+ return (_a = this.editor.contentComponent) == null ? void 0 : _a.refs[this.id];
362
+ }
363
+ updateProps(props = {}) {
364
+ Object.entries(props).forEach(([key, value]) => {
365
+ this.props[key] = value;
366
+ });
367
+ }
368
+ destroy() {
369
+ this.editor.vueRenderers.delete(this.id);
370
+ }
371
+ };
372
+
373
+ // src/VueNodeViewRenderer.ts
374
+ var nodeViewProps = {
375
+ editor: {
376
+ type: Object,
377
+ required: true
378
+ },
379
+ node: {
380
+ type: Object,
381
+ required: true
382
+ },
383
+ decorations: {
384
+ type: Object,
385
+ required: true
386
+ },
387
+ selected: {
388
+ type: Boolean,
389
+ required: true
390
+ },
391
+ extension: {
392
+ type: Object,
393
+ required: true
394
+ },
395
+ getPos: {
396
+ type: Function,
397
+ required: true
398
+ },
399
+ updateAttributes: {
400
+ type: Function,
401
+ required: true
402
+ },
403
+ deleteNode: {
404
+ type: Function,
405
+ required: true
406
+ }
407
+ };
408
+ var VueNodeView = class extends NodeView {
409
+ mount() {
410
+ const props = {
411
+ editor: this.editor,
412
+ node: this.node,
413
+ decorations: this.decorations,
414
+ selected: false,
415
+ extension: this.extension,
416
+ getPos: () => this.getPos(),
417
+ updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
418
+ deleteNode: () => this.deleteNode()
419
+ };
420
+ const onDragStart = this.onDragStart.bind(this);
421
+ this.decorationClasses = ref4(this.getDecorationClasses());
422
+ const extendedComponent = defineComponent6({
423
+ extends: { ...this.component },
424
+ props: Object.keys(props),
425
+ template: this.component.template,
426
+ setup: (reactiveProps) => {
427
+ var _a, _b;
428
+ provide("onDragStart", onDragStart);
429
+ provide("decorationClasses", this.decorationClasses);
430
+ return (_b = (_a = this.component).setup) == null ? void 0 : _b.call(_a, reactiveProps, {
431
+ expose: () => void 0
432
+ });
433
+ },
434
+ __scopeId: this.component.__scopeId,
435
+ __cssModules: this.component.__cssModules
436
+ });
437
+ this.renderer = new VueRenderer(extendedComponent, {
438
+ editor: this.editor,
439
+ props
440
+ });
441
+ }
442
+ get dom() {
443
+ if (!this.renderer.element.hasAttribute("data-node-view-wrapper")) {
444
+ throw Error("Please use the NodeViewWrapper component for your node view.");
445
+ }
446
+ return this.renderer.element;
447
+ }
448
+ get contentDOM() {
449
+ if (this.node.isLeaf) {
450
+ return null;
451
+ }
452
+ const contentElement = this.dom.querySelector("[data-node-view-content]");
453
+ return contentElement || this.dom;
454
+ }
455
+ update(node, decorations) {
456
+ const updateProps = (props) => {
457
+ this.decorationClasses.value = this.getDecorationClasses();
458
+ this.renderer.updateProps(props);
459
+ };
460
+ if (typeof this.options.update === "function") {
461
+ const oldNode = this.node;
462
+ const oldDecorations = this.decorations;
463
+ this.node = node;
464
+ this.decorations = decorations;
465
+ return this.options.update({
466
+ oldNode,
467
+ oldDecorations,
468
+ newNode: node,
469
+ newDecorations: decorations,
470
+ updateProps: () => updateProps({ node, decorations })
471
+ });
472
+ }
473
+ if (node.type !== this.node.type) {
474
+ return false;
475
+ }
476
+ if (node === this.node && this.decorations === decorations) {
477
+ return true;
478
+ }
479
+ this.node = node;
480
+ this.decorations = decorations;
481
+ updateProps({ node, decorations });
482
+ return true;
483
+ }
484
+ selectNode() {
485
+ this.renderer.updateProps({
486
+ selected: true
487
+ });
488
+ }
489
+ deselectNode() {
490
+ this.renderer.updateProps({
491
+ selected: false
492
+ });
493
+ }
494
+ getDecorationClasses() {
495
+ return this.decorations.map((item) => item.type.attrs.class).flat().join(" ");
496
+ }
497
+ destroy() {
498
+ this.renderer.destroy();
499
+ }
500
+ };
501
+ function VueNodeViewRenderer(component, options) {
502
+ return (props) => {
503
+ if (!props.editor.contentComponent) {
504
+ return {};
505
+ }
506
+ return new VueNodeView(component, props, options);
507
+ };
508
+ }
509
+
510
+ // src/index.ts
511
+ export * from "@tiptap/core";
512
+ export {
513
+ BubbleMenu,
514
+ Editor,
515
+ EditorContent,
516
+ FloatingMenu,
517
+ NodeViewContent,
518
+ NodeViewWrapper,
519
+ VueNodeViewRenderer,
520
+ VueRenderer,
521
+ nodeViewProps,
522
+ useEditor
523
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/vue-3",
3
3
  "description": "Vue components for tiptap",
4
- "version": "2.0.0-beta.21",
4
+ "version": "2.0.0-beta.211",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -12,24 +12,53 @@
12
12
  "type": "github",
13
13
  "url": "https://github.com/sponsors/ueberdosis"
14
14
  },
15
- "main": "dist/tiptap-vue-3.cjs.js",
16
- "umd": "dist/tiptap-vue-3.umd.js",
17
- "module": "dist/tiptap-vue-3.esm.js",
18
- "unpkg": "dist/tiptap-vue-3.bundle.umd.min.js",
19
- "types": "dist/packages/vue-3/src/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.js",
19
+ "require": "./dist/index.cjs"
20
+ }
21
+ },
22
+ "main": "dist/index.cjs",
23
+ "module": "dist/index.js",
24
+ "types": "dist/index.d.ts",
25
+ "type": "module",
20
26
  "files": [
21
27
  "src",
22
28
  "dist"
23
29
  ],
30
+ "devDependencies": {
31
+ "@tiptap/core": "^2.0.0-beta.211",
32
+ "@tiptap/pm": "^2.0.0-beta.211",
33
+ "vue": "^3.0.0"
34
+ },
24
35
  "peerDependencies": {
25
- "@tiptap/core": "^2.0.0-beta.1"
36
+ "@tiptap/core": "^2.0.0-beta.209",
37
+ "@tiptap/pm": "^2.0.0-beta.209",
38
+ "vue": "^3.0.0"
26
39
  },
27
40
  "dependencies": {
28
- "@tiptap/extension-bubble-menu": "^2.0.0-beta.6",
29
- "@tiptap/extension-floating-menu": "^2.0.0-beta.3",
30
- "prosemirror-state": "^1.3.4",
31
- "prosemirror-view": "^1.18.2",
32
- "vue": "^3.0.0"
41
+ "@tiptap/extension-bubble-menu": "^2.0.0-beta.211",
42
+ "@tiptap/extension-floating-menu": "^2.0.0-beta.211"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/ueberdosis/tiptap",
47
+ "directory": "packages/vue-3"
48
+ },
49
+ "sideEffects": false,
50
+ "scripts": {
51
+ "build": "tsup"
33
52
  },
34
- "gitHead": "634c1ae4931a8a268c7251f702b2a7d5121f02d2"
53
+ "tsup": {
54
+ "entry": [
55
+ "src/index.ts"
56
+ ],
57
+ "dts": true,
58
+ "splitting": true,
59
+ "format": [
60
+ "esm",
61
+ "cjs"
62
+ ]
63
+ }
35
64
  }
package/src/BubbleMenu.ts CHANGED
@@ -1,45 +1,71 @@
1
+ import { BubbleMenuPlugin, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
1
2
  import {
3
+ defineComponent,
2
4
  h,
3
- ref,
4
- PropType,
5
- onMounted,
6
5
  onBeforeUnmount,
7
- defineComponent,
6
+ onMounted,
7
+ PropType,
8
+ ref,
8
9
  } from 'vue'
9
- import {
10
- BubbleMenuPlugin,
11
- BubbleMenuPluginKey,
12
- BubbleMenuPluginProps,
13
- } from '@tiptap/extension-bubble-menu'
14
10
 
15
11
  export const BubbleMenu = defineComponent({
16
12
  name: 'BubbleMenu',
17
13
 
18
14
  props: {
15
+ pluginKey: {
16
+ // TODO: TypeScript breaks :(
17
+ // type: [String, Object as PropType<Exclude<BubbleMenuPluginProps['pluginKey'], string>>],
18
+ type: null,
19
+ default: 'bubbleMenu',
20
+ },
21
+
19
22
  editor: {
20
23
  type: Object as PropType<BubbleMenuPluginProps['editor']>,
21
24
  required: true,
22
25
  },
23
26
 
24
- keepInBounds: {
25
- type: Boolean as PropType<BubbleMenuPluginProps['keepInBounds']>,
26
- default: true,
27
+ updateDelay: {
28
+ type: Number as PropType<BubbleMenuPluginProps['updateDelay']>,
29
+ default: undefined,
30
+ },
31
+
32
+ tippyOptions: {
33
+ type: Object as PropType<BubbleMenuPluginProps['tippyOptions']>,
34
+ default: () => ({}),
35
+ },
36
+
37
+ shouldShow: {
38
+ type: Function as PropType<Exclude<Required<BubbleMenuPluginProps>['shouldShow'], null>>,
39
+ default: null,
27
40
  },
28
41
  },
29
42
 
30
- setup({ editor, keepInBounds }, { slots }) {
43
+ setup(props, { slots }) {
31
44
  const root = ref<HTMLElement | null>(null)
32
45
 
33
46
  onMounted(() => {
47
+ const {
48
+ updateDelay,
49
+ editor,
50
+ pluginKey,
51
+ shouldShow,
52
+ tippyOptions,
53
+ } = props
54
+
34
55
  editor.registerPlugin(BubbleMenuPlugin({
56
+ updateDelay,
35
57
  editor,
36
58
  element: root.value as HTMLElement,
37
- keepInBounds,
59
+ pluginKey,
60
+ shouldShow,
61
+ tippyOptions,
38
62
  }))
39
63
  })
40
64
 
41
65
  onBeforeUnmount(() => {
42
- editor.unregisterPlugin(BubbleMenuPluginKey)
66
+ const { pluginKey, editor } = props
67
+
68
+ editor.unregisterPlugin(pluginKey)
43
69
  })
44
70
 
45
71
  return () => h('div', { ref: root }, slots.default?.())