@milkdown/vue 6.1.4 → 6.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milkdown/vue",
3
- "version": "6.1.4",
3
+ "version": "6.3.0",
4
4
  "type": "module",
5
5
  "main": "./lib/index.es.js",
6
6
  "types": "./lib/index.d.ts",
@@ -16,9 +16,10 @@
16
16
  "vue"
17
17
  ],
18
18
  "dependencies": {
19
- "@milkdown/utils": "6.1.4",
20
- "nanoid": "^3.1.25",
21
- "tslib": "^2.3.1"
19
+ "@milkdown/utils": "6.3.0",
20
+ "@milkdown/exception": "6.3.0",
21
+ "nanoid": "^4.0.0",
22
+ "tslib": "^2.4.0"
22
23
  },
23
24
  "peerDependencies": {
24
25
  "@milkdown/core": "^6.0.1",
@@ -26,8 +27,8 @@
26
27
  "vue": "^3.0.0"
27
28
  },
28
29
  "devDependencies": {
29
- "@milkdown/core": "6.1.4",
30
- "@milkdown/prose": "6.1.4",
30
+ "@milkdown/core": "6.3.0",
31
+ "@milkdown/prose": "6.3.0",
31
32
  "vue": "^3.0.0"
32
33
  },
33
34
  "nx": {
package/src/Editor.tsx CHANGED
@@ -1,83 +1,23 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import { Ctx, Editor, editorViewCtx, rootCtx } from '@milkdown/core';
3
- import { ViewFactory } from '@milkdown/prose';
4
2
  import {
5
3
  ComponentInternalInstance,
6
4
  DefineComponent,
7
5
  defineComponent,
6
+ effect,
8
7
  getCurrentInstance,
9
8
  h,
10
- inject,
11
9
  InjectionKey,
12
10
  markRaw,
13
11
  onBeforeMount,
14
- onMounted,
15
12
  onUnmounted,
16
13
  provide,
17
- Ref,
18
- ref,
19
14
  shallowReactive,
20
15
  } from 'vue';
21
16
 
22
- import { AnyVueComponent } from './utils';
23
- import { createVueView, RenderOptions } from './VueNodeView';
24
-
25
- const rendererKey: InjectionKey<(component: DefineComponent, options?: RenderOptions) => (ctx: Ctx) => ViewFactory> =
26
- Symbol();
27
-
28
- type GetEditor = (
29
- container: HTMLDivElement,
30
- renderVue: (Component: AnyVueComponent, options?: RenderOptions) => (ctx: Ctx) => ViewFactory,
31
- ) => Editor;
32
-
33
- const useGetEditor = (getEditor: GetEditor) => {
34
- const divRef = ref<HTMLDivElement | null>(null);
35
- const renderVue = inject<(Component: DefineComponent, options?: RenderOptions) => (ctx: Ctx) => ViewFactory>(
36
- rendererKey,
37
- () => {
38
- throw new Error();
39
- },
40
- );
41
- const editorRef = markRaw<{ editor?: Editor }>({});
42
- onMounted(() => {
43
- if (!divRef.value) return;
44
-
45
- getEditor(divRef.value, renderVue)
46
- .create()
47
- .then((editor) => {
48
- editorRef.editor = editor;
49
- return;
50
- })
51
- .catch((e) => console.error(e));
52
- });
53
- onUnmounted(() => {
54
- const view = editorRef.editor?.action((ctx) => ctx.get(editorViewCtx));
55
- const root = editorRef.editor?.action((ctx) => ctx.get(rootCtx)) as HTMLElement;
56
-
57
- root?.firstChild?.remove();
58
- view?.destroy();
59
- });
60
-
61
- return { divRef, editorRef };
62
- };
63
-
64
- export const EditorComponent = defineComponent<{ editor: GetEditor; editorRef?: Ref<EditorRef> }>({
65
- name: 'milkdown-dom-root',
66
- setup: (props, { slots }) => {
67
- const refs = useGetEditor(props.editor);
68
- if (props.editorRef) {
69
- props.editorRef.value = {
70
- get: () => refs.editorRef.editor,
71
- dom: () => refs.divRef.value,
72
- };
73
- }
74
-
75
- return () => <div ref={refs.divRef}>{slots['default']?.()}</div>;
76
- },
77
- });
78
- EditorComponent['props'] = ['editor', 'editorRef'];
79
-
80
- export type EditorRef = { get: () => Editor | undefined; dom: () => HTMLDivElement | null };
17
+ import { EditorComponent, EditorRef } from './EditorComponent';
18
+ import { EditorInfo, EditorInfoCtx } from './types';
19
+ import { rendererKey } from './useGetEditor';
20
+ import { createVueView } from './VueNodeView';
81
21
 
82
22
  const rootInstance: {
83
23
  instance: null | ComponentInternalInstance;
@@ -88,8 +28,35 @@ export const getRootInstance = () => {
88
28
  return rootInstance.instance;
89
29
  };
90
30
 
31
+ export const editorInfoCtxKey: InjectionKey<EditorInfoCtx> = Symbol();
32
+
33
+ const refDeprecatedInfo = `
34
+ @milkdown/vue:
35
+ Passing ref to VueEditor will soon be deprecated, please use:
36
+
37
+ const { editor, getInstance, getDom, loading } = useEditor(/* creator */);
38
+
39
+ effect(() => {
40
+ if (!loading) {
41
+ const editor = getInstance();
42
+ const rootDOM = getDom();
43
+ }
44
+ })
45
+
46
+ <VueEditor editor={editor} />
47
+ `;
48
+
49
+ const compositionDeprecatedInfo = `
50
+ @milkdown/vue:
51
+ Passing editor directly to VueEditor will soon be deprecated, please use:
52
+
53
+ const { editor } = useEditor(/* creator */);
54
+
55
+ <VueEditor editor={editor} />
56
+ `;
57
+
91
58
  type PortalPair = [key: string, component: DefineComponent];
92
- export const VueEditor = defineComponent<{ editor: GetEditor; editorRef?: Ref<EditorRef> }>({
59
+ export const VueEditor = defineComponent<{ editor: EditorInfo; editorRef?: EditorRef }>({
93
60
  name: 'milkdown-vue-root',
94
61
  setup: (props) => {
95
62
  const portals = shallowReactive<PortalPair[]>([]);
@@ -113,12 +80,35 @@ export const VueEditor = defineComponent<{ editor: GetEditor; editorRef?: Ref<Ed
113
80
  portals.splice(index, 1);
114
81
  });
115
82
  const renderVue = createVueView(addPortal, removePortalByKey);
83
+
116
84
  provide(rendererKey, renderVue);
117
85
 
86
+ const usingDeprecatedCompositionAPI = Object.hasOwnProperty.call(props.editor, 'getInstance');
87
+
88
+ const { getEditorCallback, dom, editor, loading } = usingDeprecatedCompositionAPI
89
+ ? // @ts-expect-error deprecated old composition API
90
+ (props.editor.editor as EditorInfo)
91
+ : props.editor;
92
+
93
+ effect(() => {
94
+ if (usingDeprecatedCompositionAPI) {
95
+ console.warn(compositionDeprecatedInfo);
96
+ }
97
+ if (props.editorRef) {
98
+ console.warn(refDeprecatedInfo);
99
+ }
100
+ });
101
+
102
+ provide(editorInfoCtxKey, {
103
+ dom,
104
+ editor,
105
+ loading,
106
+ });
107
+
118
108
  return () => {
119
109
  const portalElements = portals.map(([id, P]) => <P key={id} />);
120
110
  return (
121
- <EditorComponent editorRef={props.editorRef} editor={props.editor}>
111
+ <EditorComponent editorRef={props.editorRef} editor={getEditorCallback.value}>
122
112
  {portalElements}
123
113
  </EditorComponent>
124
114
  );
@@ -126,7 +116,3 @@ export const VueEditor = defineComponent<{ editor: GetEditor; editorRef?: Ref<Ed
126
116
  },
127
117
  });
128
118
  VueEditor['props'] = ['editor', 'editorRef'];
129
-
130
- export const useEditor = (getEditor: GetEditor) => {
131
- return (...args: Parameters<GetEditor>) => getEditor(...args);
132
- };
@@ -0,0 +1,25 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { Editor } from '@milkdown/core';
3
+ import { defineComponent, h, inject } from 'vue';
4
+
5
+ import { editorInfoCtxKey } from './Editor';
6
+ import { EditorInfoCtx, GetEditor } from './types';
7
+ import { useGetEditor } from './useGetEditor';
8
+
9
+ export const EditorComponent = defineComponent<{ editor: GetEditor; editorRef?: EditorRef }>({
10
+ name: 'milkdown-dom-root',
11
+ setup: (props, { slots }) => {
12
+ useGetEditor(props.editor);
13
+ const ctx = inject(editorInfoCtxKey, {} as EditorInfoCtx);
14
+
15
+ if (props.editorRef) {
16
+ props.editorRef.get = () => ctx.editor.value;
17
+ props.editorRef.dom = () => ctx.dom.value;
18
+ }
19
+
20
+ return () => <div ref={ctx.dom}>{slots['default']?.()}</div>;
21
+ },
22
+ });
23
+ EditorComponent['props'] = ['editor', 'editorRef'];
24
+
25
+ export type EditorRef = { get: () => Editor | undefined; dom: () => HTMLDivElement | null };
package/src/VueNode.tsx CHANGED
@@ -2,18 +2,21 @@
2
2
  import { Ctx } from '@milkdown/core';
3
3
  import { Mark, Node } from '@milkdown/prose/model';
4
4
  import { Decoration, EditorView } from '@milkdown/prose/view';
5
- import { defineComponent, h, InjectionKey, provide } from 'vue';
5
+ import { defineComponent, h, inject, InjectionKey, provide, Ref } from 'vue';
6
6
 
7
- export type NodeContext = {
7
+ export type NodeContext<T extends Node | Mark = Node | Mark> = {
8
8
  ctx: Ctx;
9
- node: Node | Mark;
9
+ node: Ref<T>;
10
10
  view: EditorView;
11
- getPos: boolean | (() => number);
12
- decorations: Decoration[];
11
+ getPos: T extends Mark ? boolean : T extends Node ? () => number : boolean | (() => number);
12
+ decorations: Ref<readonly Decoration[]>;
13
13
  };
14
14
 
15
15
  export const nodeMetadata: InjectionKey<NodeContext> = Symbol();
16
16
 
17
+ export type UseNodeCtx = <T extends Node | Mark = Node | Mark>() => NodeContext<T>;
18
+ export const useNodeCtx: UseNodeCtx = () => inject(nodeMetadata) as NodeContext<never>;
19
+
17
20
  export const VueNodeContainer = defineComponent<NodeContext & { as: string }>({
18
21
  name: 'milkdown-node-container',
19
22
  setup: ({ node, view, getPos, decorations, ctx, as }, context) => {
@@ -1,10 +1,16 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
2
  import { Ctx } from '@milkdown/core';
3
- import type { ViewFactory } from '@milkdown/prose';
4
3
  import { Mark, Node } from '@milkdown/prose/model';
5
- import type { Decoration, DecorationSet, EditorView, NodeView } from '@milkdown/prose/view';
4
+ import type {
5
+ Decoration,
6
+ DecorationSource,
7
+ EditorView,
8
+ MarkViewConstructor,
9
+ NodeView,
10
+ NodeViewConstructor,
11
+ } from '@milkdown/prose/view';
6
12
  import { customAlphabet } from 'nanoid';
7
- import { DefineComponent, defineComponent, h, markRaw, Teleport } from 'vue';
13
+ import { DefineComponent, defineComponent, h, markRaw, Ref, ref, Teleport } from 'vue';
8
14
 
9
15
  import { getRootInstance } from '.';
10
16
  import { Content, VueNodeContainer } from './VueNode';
@@ -14,12 +20,16 @@ const nanoid = customAlphabet('abcedfghicklmn', 10);
14
20
  export type RenderOptions = Partial<
15
21
  {
16
22
  as: string;
17
- } & Pick<NodeView, 'ignoreMutation' | 'deselectNode' | 'selectNode' | 'destroy' | 'update'>
23
+ update?: (node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource) => boolean;
24
+ } & Pick<NodeView, 'ignoreMutation' | 'deselectNode' | 'selectNode' | 'destroy'>
18
25
  >;
19
26
 
20
27
  export const createVueView =
21
28
  (addPortal: (portal: DefineComponent, key: string) => void, removePortalByKey: (key: string) => void) =>
22
- (component: DefineComponent, options: RenderOptions = {}): ((ctx: Ctx) => ViewFactory) =>
29
+ (
30
+ component: DefineComponent,
31
+ options: RenderOptions = {},
32
+ ): ((ctx: Ctx) => NodeViewConstructor | MarkViewConstructor) =>
23
33
  (ctx) =>
24
34
  (node, view, getPos, decorations) =>
25
35
  new VueNodeView(ctx, component, addPortal, removePortalByKey, options, node, view, getPos, decorations);
@@ -29,65 +39,76 @@ export class VueNodeView implements NodeView {
29
39
  key: string;
30
40
 
31
41
  get isInlineOrMark() {
32
- return this.node instanceof Mark || this.node.isInline;
42
+ return this.node.value instanceof Mark || this.node.value.isInline;
33
43
  }
34
44
 
45
+ private node: Ref<Node | Mark>;
46
+ private decorations: Ref<readonly Decoration[]>;
47
+
35
48
  constructor(
36
49
  private ctx: Ctx,
37
50
  private component: DefineComponent,
38
51
  private addPortal: (portal: DefineComponent, key: string) => void,
39
52
  private removePortalByKey: (key: string) => void,
40
53
  private options: RenderOptions,
41
- private node: Node | Mark,
54
+ node: Node | Mark,
42
55
  private view: EditorView,
43
56
  private getPos: boolean | (() => number),
44
- private decorations: Decoration[],
57
+ decorations: readonly Decoration[],
45
58
  ) {
46
59
  this.key = nanoid();
60
+ this.node = ref(node);
61
+ this.decorations = ref(decorations);
47
62
  const elementName = options.as ? options.as : this.isInlineOrMark ? 'span' : 'div';
48
63
  this.teleportDOM = document.createElement(elementName);
49
64
  this.renderPortal();
50
65
  }
51
66
 
52
67
  get dom() {
53
- return this.teleportDOM.firstElementChild || this.teleportDOM;
68
+ return (this.teleportDOM.firstElementChild || this.teleportDOM) as HTMLElement;
54
69
  }
55
70
 
56
71
  get contentDOM() {
57
72
  if (this.node instanceof Node && this.node.isLeaf) {
58
- return null;
73
+ return undefined;
59
74
  }
60
75
 
61
- return this.teleportDOM.querySelector('[data-view-content]') || this.dom;
76
+ return this.teleportDOM.querySelector<HTMLElement>('[data-view-content]') || this.dom;
62
77
  }
63
78
 
79
+ getPortal = (): DefineComponent => {
80
+ const CustomComponent = this.component;
81
+ const elementName = this.options.as ? this.options.as : this.isInlineOrMark ? 'span' : 'div';
82
+ return markRaw(
83
+ defineComponent({
84
+ name: 'milkdown-portal',
85
+ setup: () => {
86
+ return () => (
87
+ <Teleport key={this.key} to={this.teleportDOM}>
88
+ <VueNodeContainer
89
+ as={elementName}
90
+ ctx={this.ctx}
91
+ node={this.node}
92
+ view={this.view}
93
+ getPos={this.getPos}
94
+ decorations={this.decorations}
95
+ >
96
+ <CustomComponent>
97
+ <Content isInline={this.isInlineOrMark} />
98
+ </CustomComponent>
99
+ </VueNodeContainer>
100
+ </Teleport>
101
+ );
102
+ },
103
+ }) as DefineComponent,
104
+ );
105
+ };
106
+
64
107
  renderPortal() {
65
108
  if (!this.teleportDOM) return;
66
109
 
67
- const CustomComponent = this.component;
68
- const elementName = this.options.as ? this.options.as : this.isInlineOrMark ? 'span' : 'div';
69
- const Portal = defineComponent({
70
- name: 'milkdown-portal',
71
- setup: () => {
72
- return () => (
73
- <Teleport key={this.key} to={this.teleportDOM}>
74
- <VueNodeContainer
75
- as={elementName}
76
- ctx={this.ctx}
77
- node={this.node}
78
- view={this.view}
79
- getPos={this.getPos}
80
- decorations={this.decorations}
81
- >
82
- <CustomComponent>
83
- <Content isInline={this.isInlineOrMark} />
84
- </CustomComponent>
85
- </VueNodeContainer>
86
- </Teleport>
87
- );
88
- },
89
- });
90
- this.addPortal(markRaw(Portal) as DefineComponent, this.key);
110
+ const Portal = this.getPortal();
111
+ this.addPortal(Portal, this.key);
91
112
  const instance = getRootInstance();
92
113
  if (instance) {
93
114
  instance.update();
@@ -96,10 +117,11 @@ export class VueNodeView implements NodeView {
96
117
 
97
118
  destroy() {
98
119
  this.options.destroy?.();
120
+ this.teleportDOM.remove();
99
121
  this.removePortalByKey(this.key);
100
122
  }
101
123
 
102
- ignoreMutation(mutation: MutationRecord | { type: 'selection'; target: Element }) {
124
+ ignoreMutation(mutation: MutationRecord) {
103
125
  if (this.options.ignoreMutation) {
104
126
  return this.options.ignoreMutation(mutation);
105
127
  }
@@ -113,7 +135,7 @@ export class VueNodeView implements NodeView {
113
135
  }
114
136
  }
115
137
 
116
- if (mutation.type === 'selection') {
138
+ if ((mutation as unknown as { type: string }).type === 'selection') {
117
139
  return false;
118
140
  }
119
141
 
@@ -128,24 +150,30 @@ export class VueNodeView implements NodeView {
128
150
  return true;
129
151
  }
130
152
 
131
- update(node: Node, decorations: Decoration[], innerDecorations: DecorationSet) {
132
- if (this.options.update) {
133
- const result = this.options.update?.(node, decorations, innerDecorations);
134
- if (result != null) {
135
- return result;
153
+ update(node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource) {
154
+ const innerUpdate = () => {
155
+ if (this.options.update) {
156
+ const result = this.options.update?.(node, decorations, innerDecorations);
157
+ if (result != null) {
158
+ return result;
159
+ }
160
+ }
161
+ if (this.node.value.type !== node.type) {
162
+ return false;
163
+ }
164
+
165
+ if (node === this.node.value && this.decorations.value === decorations) {
166
+ return true;
136
167
  }
137
- }
138
- if (this.node.type !== node.type) {
139
- return false;
140
- }
141
168
 
142
- if (node === this.node && this.decorations === decorations) {
169
+ this.node.value = node;
170
+ this.decorations.value = decorations;
143
171
  return true;
144
- }
172
+ };
145
173
 
146
- this.node = node;
147
- this.decorations = decorations;
148
- return true;
174
+ const shouldUpdate = innerUpdate();
175
+
176
+ return shouldUpdate;
149
177
  }
150
178
 
151
179
  selectNode = this.options?.selectNode;
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
2
  export * from './Editor';
3
- export { nodeMetadata } from './VueNode';
3
+ export * from './types';
4
+ export * from './useEditor';
5
+ export * from './VueNode';
package/src/types.ts ADDED
@@ -0,0 +1,50 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { Ctx, Editor } from '@milkdown/core';
3
+ import type { Mark, Node } from '@milkdown/prose/model';
4
+ import { Decoration, DecorationSource, MarkViewConstructor, NodeView, NodeViewConstructor } from '@milkdown/prose/view';
5
+ import { Ref } from 'vue';
6
+
7
+ import { AnyVueComponent } from './utils';
8
+
9
+ export type RenderOptions = Partial<
10
+ {
11
+ as: string;
12
+ update?: (node: Node, decorations: readonly Decoration[], innerDecorations: DecorationSource) => boolean;
13
+ } & Pick<NodeView, 'ignoreMutation' | 'deselectNode' | 'selectNode' | 'destroy'>
14
+ >;
15
+
16
+ export type RenderVue<U = never> = <T extends Node | Mark = Node | Mark>(
17
+ Component: AnyVueComponent,
18
+ options?: RenderOptions,
19
+ ) => (
20
+ ctx: Ctx,
21
+ ) => U extends never
22
+ ? T extends Node
23
+ ? NodeViewConstructor
24
+ : T extends Mark
25
+ ? MarkViewConstructor
26
+ : NodeViewConstructor & MarkViewConstructor
27
+ : U extends Node
28
+ ? NodeViewConstructor
29
+ : U extends Mark
30
+ ? MarkViewConstructor
31
+ : NodeViewConstructor & MarkViewConstructor;
32
+
33
+ export type GetEditor = (container: HTMLDivElement, renderVue: RenderVue) => Editor;
34
+
35
+ export type EditorInfoCtx = {
36
+ dom: Ref<HTMLDivElement | null>;
37
+ editor: Ref<Editor | undefined>;
38
+ loading: Ref<boolean>;
39
+ };
40
+
41
+ export type EditorInfo = {
42
+ getEditorCallback: Ref<GetEditor>;
43
+ } & EditorInfoCtx;
44
+
45
+ export type UseEditorReturn = {
46
+ loading: Ref<boolean>;
47
+ getInstance: () => Editor | undefined;
48
+ getDom: () => HTMLDivElement | null;
49
+ editor: EditorInfo;
50
+ };
@@ -0,0 +1,25 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+
3
+ import { Editor } from '@milkdown/core';
4
+ import { ref } from 'vue';
5
+
6
+ import { GetEditor, UseEditorReturn } from './types';
7
+
8
+ export const useEditor = (getEditor: GetEditor): UseEditorReturn => {
9
+ const dom = ref<HTMLDivElement | null>(null);
10
+ const editor = ref<Editor>();
11
+ const loading = ref(true);
12
+ const getEditorCallback = ref<GetEditor>((...args) => getEditor(...args));
13
+
14
+ return {
15
+ loading,
16
+ getInstance: () => editor.value,
17
+ getDom: () => dom.value,
18
+ editor: {
19
+ getEditorCallback,
20
+ dom,
21
+ editor,
22
+ loading,
23
+ },
24
+ };
25
+ };
@@ -0,0 +1,51 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { Ctx, editorViewCtx, rootCtx } from '@milkdown/core';
3
+ import { vueRendererCallOutOfScope } from '@milkdown/exception';
4
+ import { MarkViewConstructor, NodeViewConstructor } from '@milkdown/prose/view';
5
+ import { DefineComponent, inject, InjectionKey, onMounted, onUnmounted, ref } from 'vue';
6
+
7
+ import { editorInfoCtxKey } from '.';
8
+ import { EditorInfoCtx, GetEditor, RenderOptions, RenderVue } from './types';
9
+
10
+ export const rendererKey: InjectionKey<
11
+ (component: DefineComponent, options?: RenderOptions) => (ctx: Ctx) => NodeViewConstructor | MarkViewConstructor
12
+ > = Symbol();
13
+
14
+ export const useGetEditor = (getEditor: GetEditor) => {
15
+ const renderVue = inject<RenderVue>(rendererKey, () => {
16
+ throw vueRendererCallOutOfScope();
17
+ });
18
+ const { dom, loading, editor: editorRef } = inject(editorInfoCtxKey, {} as EditorInfoCtx);
19
+ const lock = ref(false);
20
+
21
+ onMounted(() => {
22
+ if (!dom.value) return;
23
+
24
+ const editor = getEditor(dom.value, renderVue);
25
+ if (!editor) return;
26
+
27
+ if (lock.value) return;
28
+
29
+ loading.value = true;
30
+ lock.value = true;
31
+
32
+ editor
33
+ .create()
34
+ .then((editor) => {
35
+ editorRef.value = editor;
36
+ return;
37
+ })
38
+ .finally(() => {
39
+ loading.value = false;
40
+ lock.value = false;
41
+ })
42
+ .catch((e) => console.error(e));
43
+ });
44
+ onUnmounted(() => {
45
+ const view = editorRef.value?.action((ctx) => ctx.get(editorViewCtx));
46
+ const root = editorRef.value?.action((ctx) => ctx.get(rootCtx)) as HTMLElement;
47
+
48
+ root?.firstChild?.remove();
49
+ view?.destroy();
50
+ });
51
+ };