@blockslides/vue-3 0.2.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/LICENSE.md +36 -0
- package/dist/Editor.d.ts +24 -0
- package/dist/EditorContent.d.ts +18 -0
- package/dist/FloatingMenu-BKkixozS.js +226 -0
- package/dist/FloatingMenu-By8Qi7tW.cjs +1 -0
- package/dist/NodeViewContent.d.ts +13 -0
- package/dist/NodeViewWrapper.d.ts +13 -0
- package/dist/VueMarkViewRenderer.d.ts +63 -0
- package/dist/VueNodeViewRenderer.d.ts +63 -0
- package/dist/VueRenderer.d.ts +35 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +718 -0
- package/dist/menus/BubbleMenu.d.ts +6 -0
- package/dist/menus/FloatingMenu.d.ts +6 -0
- package/dist/menus/index.d.ts +3 -0
- package/dist/menus.cjs +1 -0
- package/dist/menus.d.ts +1 -0
- package/dist/menus.js +6 -0
- package/dist/useEditor.d.ts +4 -0
- package/dist/useSlideEditor.d.ts +36 -0
- package/package.json +69 -0
- package/src/Editor.ts +93 -0
- package/src/EditorContent.ts +77 -0
- package/src/NodeViewContent.ts +21 -0
- package/src/NodeViewWrapper.ts +31 -0
- package/src/SlideEditor.vue +54 -0
- package/src/VueMarkViewRenderer.ts +130 -0
- package/src/VueNodeViewRenderer.ts +317 -0
- package/src/VueRenderer.ts +104 -0
- package/src/index.ts +12 -0
- package/src/menus/BubbleMenu.ts +111 -0
- package/src/menus/BubbleMenuPreset.vue +137 -0
- package/src/menus/FloatingMenu.ts +84 -0
- package/src/menus/index.ts +3 -0
- package/src/useEditor.ts +24 -0
- package/src/useSlideEditor.ts +255 -0
- package/src/vue-shims.d.ts +5 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BubbleMenuPluginProps } from '../../../extension-bubble-menu/src';
|
|
2
|
+
|
|
3
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
4
|
+
export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'>;
|
|
5
|
+
export declare const BubbleMenu: any;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FloatingMenuPluginProps } from '../../../extension-floating-menu/src';
|
|
2
|
+
|
|
3
|
+
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
|
|
4
|
+
export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element'>;
|
|
5
|
+
export declare const FloatingMenu: any;
|
|
6
|
+
export {};
|
package/dist/menus.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./FloatingMenu-By8Qi7tW.cjs");exports.BubbleMenu=e.BubbleMenu;exports.BubbleMenuPreset=e._sfc_main;exports.FloatingMenu=e.FloatingMenu;
|
package/dist/menus.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './menus/index'
|
package/dist/menus.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { templatesV1 } from '../../ai-context/src';
|
|
2
|
+
import { AnyExtension, Editor, JSONContent, EditorOptions } from '../../core/src';
|
|
3
|
+
import { ExtensionKitOptions } from '../../extension-kit/src';
|
|
4
|
+
|
|
5
|
+
type PresetTemplates = ReturnType<typeof templatesV1.listPresetTemplates>;
|
|
6
|
+
export interface UseSlideEditorProps extends Omit<Partial<EditorOptions>, 'extensions'> {
|
|
7
|
+
/**
|
|
8
|
+
* Initial content for the editor. If omitted, a single preset slide is used.
|
|
9
|
+
*/
|
|
10
|
+
content?: EditorOptions['content'];
|
|
11
|
+
/**
|
|
12
|
+
* Called on every update with the current JSON document.
|
|
13
|
+
*/
|
|
14
|
+
onChange?: (doc: JSONContent, editor: Editor) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Additional extensions to append after the ExtensionKit bundle.
|
|
17
|
+
*/
|
|
18
|
+
extensions?: AnyExtension[];
|
|
19
|
+
/**
|
|
20
|
+
* Customize or disable pieces of ExtensionKit (e.g., bubbleMenu: false).
|
|
21
|
+
*/
|
|
22
|
+
extensionKitOptions?: ExtensionKitOptions;
|
|
23
|
+
/**
|
|
24
|
+
* Optional preset list to power the add-slide button.
|
|
25
|
+
*/
|
|
26
|
+
presetTemplates?: PresetTemplates;
|
|
27
|
+
/**
|
|
28
|
+
* Called once when an editor instance is ready.
|
|
29
|
+
*/
|
|
30
|
+
onEditorReady?: (editor: Editor) => void;
|
|
31
|
+
}
|
|
32
|
+
export declare const useSlideEditor: (props?: UseSlideEditorProps) => {
|
|
33
|
+
editor: import('vue').ShallowRef<import('./Editor.js').Editor | undefined, import('./Editor.js').Editor | undefined>;
|
|
34
|
+
presets: import('vue').ComputedRef<templatesV1.PresetTemplate[]>;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@blockslides/vue-3",
|
|
3
|
+
"description": "Vue 3 components for blockslides",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"homepage": "https://github.com/keivanmojmali/blockslides",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"blockslides",
|
|
8
|
+
"blockslides vue components",
|
|
9
|
+
"editor",
|
|
10
|
+
"wysiwyg"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": {
|
|
16
|
+
"import": "./dist/index.d.ts",
|
|
17
|
+
"require": "./dist/index.d.cts"
|
|
18
|
+
},
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"require": "./dist/index.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./menus": {
|
|
23
|
+
"types": {
|
|
24
|
+
"import": "./dist/menus/index.d.ts",
|
|
25
|
+
"require": "./dist/menus/index.d.cts"
|
|
26
|
+
},
|
|
27
|
+
"import": "./dist/menus/index.js",
|
|
28
|
+
"require": "./dist/menus/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"main": "dist/index.cjs",
|
|
32
|
+
"module": "dist/index.js",
|
|
33
|
+
"types": "dist/index.d.ts",
|
|
34
|
+
"type": "module",
|
|
35
|
+
"files": [
|
|
36
|
+
"src",
|
|
37
|
+
"dist"
|
|
38
|
+
],
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@floating-ui/dom": "^1.0.0",
|
|
41
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
42
|
+
"vite": "^5.0.0",
|
|
43
|
+
"vite-plugin-dts": "^3.0.0",
|
|
44
|
+
"vue": "^3.5.13",
|
|
45
|
+
"@blockslides/core": "^0.3.2",
|
|
46
|
+
"@blockslides/pm": "^0.1.1"
|
|
47
|
+
},
|
|
48
|
+
"optionalDependencies": {
|
|
49
|
+
"@blockslides/extension-bubble-menu": "^0.1.1",
|
|
50
|
+
"@blockslides/extension-floating-menu": "^0.1.1"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@floating-ui/dom": "^1.0.0",
|
|
54
|
+
"vue": "^3.0.0",
|
|
55
|
+
"@blockslides/core": "^0.3.2",
|
|
56
|
+
"@blockslides/pm": "^0.1.1"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "https://github.com/keivanmojmali/blockslides",
|
|
61
|
+
"directory": "packages/vue-3"
|
|
62
|
+
},
|
|
63
|
+
"sideEffects": false,
|
|
64
|
+
"author": "keivanmojmali",
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "vite build",
|
|
67
|
+
"lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/Editor.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
import type { EditorOptions, Storage } from '@blockslides/core'
|
|
3
|
+
import { Editor as CoreEditor } from '@blockslides/core'
|
|
4
|
+
import type { EditorState, Plugin, PluginKey } from '@blockslides/pm/state'
|
|
5
|
+
import type { AppContext, ComponentInternalInstance, ComponentPublicInstance, Ref } from 'vue'
|
|
6
|
+
import { customRef, markRaw } from 'vue'
|
|
7
|
+
|
|
8
|
+
function useDebouncedRef<T>(value: T) {
|
|
9
|
+
return customRef<T>((track, trigger) => {
|
|
10
|
+
return {
|
|
11
|
+
get() {
|
|
12
|
+
track()
|
|
13
|
+
return value
|
|
14
|
+
},
|
|
15
|
+
set(newValue) {
|
|
16
|
+
// update state
|
|
17
|
+
value = newValue
|
|
18
|
+
|
|
19
|
+
// update view as soon as possible
|
|
20
|
+
requestAnimationFrame(() => {
|
|
21
|
+
requestAnimationFrame(() => {
|
|
22
|
+
trigger()
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type ContentComponent = ComponentInternalInstance & {
|
|
31
|
+
ctx: ComponentPublicInstance
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class Editor extends CoreEditor {
|
|
35
|
+
private reactiveState: Ref<EditorState>
|
|
36
|
+
|
|
37
|
+
private reactiveExtensionStorage: Ref<Storage>
|
|
38
|
+
|
|
39
|
+
public contentComponent: ContentComponent | null = null
|
|
40
|
+
|
|
41
|
+
public appContext: AppContext | null = null
|
|
42
|
+
|
|
43
|
+
constructor(options: Partial<EditorOptions> = {}) {
|
|
44
|
+
super(options)
|
|
45
|
+
|
|
46
|
+
this.reactiveState = useDebouncedRef(this.view.state)
|
|
47
|
+
this.reactiveExtensionStorage = useDebouncedRef(this.extensionStorage)
|
|
48
|
+
|
|
49
|
+
this.on('beforeTransaction', ({ nextState }) => {
|
|
50
|
+
this.reactiveState.value = nextState
|
|
51
|
+
this.reactiveExtensionStorage.value = this.extensionStorage
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
return markRaw(this) // eslint-disable-line
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get state() {
|
|
58
|
+
return this.reactiveState ? this.reactiveState.value : this.view.state
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get storage() {
|
|
62
|
+
return this.reactiveExtensionStorage ? this.reactiveExtensionStorage.value : super.storage
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Register a ProseMirror plugin.
|
|
67
|
+
*/
|
|
68
|
+
public registerPlugin(
|
|
69
|
+
plugin: Plugin,
|
|
70
|
+
handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[],
|
|
71
|
+
): EditorState {
|
|
72
|
+
const nextState = super.registerPlugin(plugin, handlePlugins)
|
|
73
|
+
|
|
74
|
+
if (this.reactiveState) {
|
|
75
|
+
this.reactiveState.value = nextState
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return nextState
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Unregister a ProseMirror plugin.
|
|
83
|
+
*/
|
|
84
|
+
public unregisterPlugin(nameOrPluginKey: string | PluginKey): EditorState | undefined {
|
|
85
|
+
const nextState = super.unregisterPlugin(nameOrPluginKey)
|
|
86
|
+
|
|
87
|
+
if (this.reactiveState && nextState) {
|
|
88
|
+
this.reactiveState.value = nextState
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return nextState
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { PropType, Ref } from 'vue'
|
|
2
|
+
import { defineComponent, getCurrentInstance, h, nextTick, onBeforeUnmount, ref, unref, watchEffect } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { Editor } from './Editor.js'
|
|
5
|
+
|
|
6
|
+
export const EditorContent = defineComponent({
|
|
7
|
+
name: 'EditorContent',
|
|
8
|
+
|
|
9
|
+
props: {
|
|
10
|
+
editor: {
|
|
11
|
+
default: null,
|
|
12
|
+
type: Object as PropType<Editor>,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
setup(props) {
|
|
17
|
+
const rootEl: Ref<Element | undefined> = ref()
|
|
18
|
+
const instance = getCurrentInstance()
|
|
19
|
+
|
|
20
|
+
watchEffect(() => {
|
|
21
|
+
const editor = props.editor
|
|
22
|
+
|
|
23
|
+
if (editor && editor.options.element && rootEl.value) {
|
|
24
|
+
nextTick(() => {
|
|
25
|
+
if (!rootEl.value || !editor.view.dom?.parentNode) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// TODO using the new editor.mount method might allow us to remove this
|
|
30
|
+
const element = unref(rootEl.value)
|
|
31
|
+
|
|
32
|
+
rootEl.value.append(...editor.view.dom.parentNode.childNodes)
|
|
33
|
+
|
|
34
|
+
// @ts-ignore
|
|
35
|
+
editor.contentComponent = instance.ctx._
|
|
36
|
+
|
|
37
|
+
if (instance) {
|
|
38
|
+
editor.appContext = {
|
|
39
|
+
...instance.appContext,
|
|
40
|
+
// Vue internally uses prototype chain to forward/shadow injects across the entire component chain
|
|
41
|
+
// so don't use object spread operator or 'Object.assign' and just set `provides` as is on editor's appContext
|
|
42
|
+
// @ts-expect-error forward instance's 'provides' into appContext
|
|
43
|
+
provides: instance.provides,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
editor.setOptions({
|
|
48
|
+
element,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
editor.createNodeViews()
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
onBeforeUnmount(() => {
|
|
57
|
+
const editor = props.editor
|
|
58
|
+
|
|
59
|
+
if (!editor) {
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
editor.contentComponent = null
|
|
64
|
+
editor.appContext = null
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return { rootEl }
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
render() {
|
|
71
|
+
return h('div', {
|
|
72
|
+
ref: (el: any) => {
|
|
73
|
+
this.rootEl = el
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
},
|
|
77
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineComponent, h } from 'vue'
|
|
2
|
+
|
|
3
|
+
export const NodeViewContent = defineComponent({
|
|
4
|
+
name: 'NodeViewContent',
|
|
5
|
+
|
|
6
|
+
props: {
|
|
7
|
+
as: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: 'div',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
render() {
|
|
14
|
+
return h(this.as, {
|
|
15
|
+
style: {
|
|
16
|
+
whiteSpace: 'pre-wrap',
|
|
17
|
+
},
|
|
18
|
+
'data-node-view-content': '',
|
|
19
|
+
})
|
|
20
|
+
},
|
|
21
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { defineComponent, h } from 'vue'
|
|
2
|
+
|
|
3
|
+
export const NodeViewWrapper = defineComponent({
|
|
4
|
+
name: 'NodeViewWrapper',
|
|
5
|
+
|
|
6
|
+
props: {
|
|
7
|
+
as: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: 'div',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
inject: ['onDragStart', 'decorationClasses'],
|
|
14
|
+
|
|
15
|
+
render() {
|
|
16
|
+
return h(
|
|
17
|
+
this.as,
|
|
18
|
+
{
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
class: this.decorationClasses,
|
|
21
|
+
style: {
|
|
22
|
+
whiteSpace: 'normal',
|
|
23
|
+
},
|
|
24
|
+
'data-node-view-wrapper': '',
|
|
25
|
+
// @ts-ignore (https://github.com/vuejs/vue-next/issues/3031)
|
|
26
|
+
onDragstart: this.onDragStart,
|
|
27
|
+
},
|
|
28
|
+
this.$slots.default?.(),
|
|
29
|
+
)
|
|
30
|
+
},
|
|
31
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, watch, onMounted } from 'vue'
|
|
3
|
+
import { EditorContent } from './EditorContent'
|
|
4
|
+
import BubbleMenuPreset from './menus/BubbleMenuPreset.vue'
|
|
5
|
+
import { useSlideEditor, type UseSlideEditorProps } from './useSlideEditor'
|
|
6
|
+
import type { Editor } from '@blockslides/core'
|
|
7
|
+
|
|
8
|
+
export interface BubbleMenuPresetProps {
|
|
9
|
+
editor: Editor
|
|
10
|
+
[key: string]: any
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SlideEditorProps extends UseSlideEditorProps {
|
|
14
|
+
/**
|
|
15
|
+
* Toggle or customize the built-in BubbleMenuPreset.
|
|
16
|
+
* - true (default): render with defaults
|
|
17
|
+
* - false: disable entirely
|
|
18
|
+
* - object: pass through to BubbleMenuPreset
|
|
19
|
+
*/
|
|
20
|
+
bubbleMenuPreset?: boolean | BubbleMenuPresetProps
|
|
21
|
+
className?: string
|
|
22
|
+
style?: any
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const props = withDefaults(defineProps<SlideEditorProps>(), {
|
|
26
|
+
bubbleMenuPreset: true,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const {
|
|
30
|
+
bubbleMenuPreset,
|
|
31
|
+
className,
|
|
32
|
+
style,
|
|
33
|
+
...hookProps
|
|
34
|
+
} = props
|
|
35
|
+
|
|
36
|
+
const { editor } = useSlideEditor(hookProps)
|
|
37
|
+
|
|
38
|
+
const bubbleMenuProps = computed(() => {
|
|
39
|
+
if (props.bubbleMenuPreset === false) return null
|
|
40
|
+
if (props.bubbleMenuPreset === true) return {}
|
|
41
|
+
return props.bubbleMenuPreset
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<div v-if="editor" :class="className" :style="style">
|
|
48
|
+
<div class="bs-viewport">
|
|
49
|
+
<EditorContent :editor="editor" />
|
|
50
|
+
<BubbleMenuPreset v-if="bubbleMenuProps" :editor="editor" v-bind="bubbleMenuProps" />
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/* eslint-disable no-underscore-dangle */
|
|
2
|
+
import type { MarkViewProps, MarkViewRenderer, MarkViewRendererOptions } from '@blockslides/core'
|
|
3
|
+
import { MarkView } from '@blockslides/core'
|
|
4
|
+
import type { Component, PropType } from 'vue'
|
|
5
|
+
import { defineComponent, h, toRaw } from 'vue'
|
|
6
|
+
|
|
7
|
+
import type { Editor } from './Editor.js'
|
|
8
|
+
import { VueRenderer } from './VueRenderer.js'
|
|
9
|
+
|
|
10
|
+
export interface VueMarkViewRendererOptions extends MarkViewRendererOptions {
|
|
11
|
+
as?: string
|
|
12
|
+
className?: string
|
|
13
|
+
attrs?: { [key: string]: string }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const markViewProps = {
|
|
17
|
+
editor: {
|
|
18
|
+
type: Object as PropType<MarkViewProps['editor']>,
|
|
19
|
+
required: true as const,
|
|
20
|
+
},
|
|
21
|
+
mark: {
|
|
22
|
+
type: Object as PropType<MarkViewProps['mark']>,
|
|
23
|
+
required: true as const,
|
|
24
|
+
},
|
|
25
|
+
extension: {
|
|
26
|
+
type: Object as PropType<MarkViewProps['extension']>,
|
|
27
|
+
required: true as const,
|
|
28
|
+
},
|
|
29
|
+
inline: {
|
|
30
|
+
type: Boolean as PropType<MarkViewProps['inline']>,
|
|
31
|
+
required: true as const,
|
|
32
|
+
},
|
|
33
|
+
view: {
|
|
34
|
+
type: Object as PropType<MarkViewProps['view']>,
|
|
35
|
+
required: true as const,
|
|
36
|
+
},
|
|
37
|
+
updateAttributes: {
|
|
38
|
+
type: Function as PropType<MarkViewProps['updateAttributes']>,
|
|
39
|
+
required: true as const,
|
|
40
|
+
},
|
|
41
|
+
HTMLAttributes: {
|
|
42
|
+
type: Object as PropType<MarkViewProps['HTMLAttributes']>,
|
|
43
|
+
required: true as const,
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const MarkViewContent = defineComponent({
|
|
48
|
+
name: 'MarkViewContent',
|
|
49
|
+
|
|
50
|
+
props: {
|
|
51
|
+
as: {
|
|
52
|
+
type: String,
|
|
53
|
+
default: 'span',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
render() {
|
|
58
|
+
return h(this.as, {
|
|
59
|
+
style: {
|
|
60
|
+
whiteSpace: 'inherit',
|
|
61
|
+
},
|
|
62
|
+
'data-mark-view-content': '',
|
|
63
|
+
})
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
export class VueMarkView extends MarkView<Component, VueMarkViewRendererOptions> {
|
|
68
|
+
renderer: VueRenderer
|
|
69
|
+
|
|
70
|
+
constructor(component: Component, props: MarkViewProps, options?: Partial<VueMarkViewRendererOptions>) {
|
|
71
|
+
super(component, props, options)
|
|
72
|
+
|
|
73
|
+
const componentProps = { ...props, updateAttributes: this.updateAttributes.bind(this) } satisfies MarkViewProps
|
|
74
|
+
|
|
75
|
+
// Create extended component with provide
|
|
76
|
+
const extendedComponent = defineComponent({
|
|
77
|
+
extends: { ...component },
|
|
78
|
+
props: Object.keys(componentProps),
|
|
79
|
+
template: (this.component as any).template,
|
|
80
|
+
setup: reactiveProps => {
|
|
81
|
+
return (component as any).setup?.(reactiveProps, {
|
|
82
|
+
expose: () => undefined,
|
|
83
|
+
})
|
|
84
|
+
},
|
|
85
|
+
// Add support for scoped styles
|
|
86
|
+
__scopeId: (component as any).__scopeId,
|
|
87
|
+
__cssModules: (component as any).__cssModules,
|
|
88
|
+
__name: (component as any).__name,
|
|
89
|
+
__file: (component as any).__file,
|
|
90
|
+
})
|
|
91
|
+
this.renderer = new VueRenderer(extendedComponent, {
|
|
92
|
+
editor: this.editor,
|
|
93
|
+
props: componentProps,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get dom() {
|
|
98
|
+
return this.renderer.element as HTMLElement
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get contentDOM() {
|
|
102
|
+
return this.dom.querySelector('[data-mark-view-content]') as HTMLElement | null
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
updateAttributes(attrs: Record<string, any>): void {
|
|
106
|
+
// since this.mark is now an proxy, we need to get the actual mark from it
|
|
107
|
+
const unproxiedMark = toRaw(this.mark)
|
|
108
|
+
super.updateAttributes(attrs, unproxiedMark)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
destroy() {
|
|
112
|
+
this.renderer.destroy()
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function VueMarkViewRenderer(
|
|
117
|
+
component: Component,
|
|
118
|
+
options: Partial<VueMarkViewRendererOptions> = {},
|
|
119
|
+
): MarkViewRenderer {
|
|
120
|
+
return props => {
|
|
121
|
+
// try to get the parent component
|
|
122
|
+
// this is important for vue devtools to show the component hierarchy correctly
|
|
123
|
+
// maybe it's `undefined` because <editor-content> isn't rendered yet
|
|
124
|
+
if (!(props.editor as Editor).contentComponent) {
|
|
125
|
+
return {} as unknown as MarkView<any, any>
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return new VueMarkView(component, props, options)
|
|
129
|
+
}
|
|
130
|
+
}
|