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