forked from halo-sigs/richtext-editor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
EditorBubbleMenu.vue
103 lines (96 loc) · 2.69 KB
/
EditorBubbleMenu.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<script lang="ts" setup>
import type { PropType } from "vue";
import type { Editor, AnyExtension } from "@tiptap/core";
import BubbleMenu from "@/components/bubble/BubbleMenu.vue";
import type { NodeBubbleMenu } from "@/types";
import BubbleItem from "@/components/bubble/BubbleItem.vue";
import type { EditorView } from "prosemirror-view";
import type { EditorState } from "prosemirror-state";
const props = defineProps({
editor: {
type: Object as PropType<Editor>,
required: true,
},
});
const getBubbleMenuFromExtensions = () => {
const extensionManager = props.editor?.extensionManager;
return extensionManager.extensions
.map((extension: AnyExtension) => {
const { getBubbleMenu } = extension.options;
if (!getBubbleMenu) {
return null;
}
const nodeBubbleMenu = getBubbleMenu({
editor: props.editor,
}) as NodeBubbleMenu;
if (nodeBubbleMenu.items) {
nodeBubbleMenu.items = nodeBubbleMenu.items.sort(
(a, b) => a.priority - b.priority
);
}
return nodeBubbleMenu;
})
.filter(Boolean) as NodeBubbleMenu[];
};
const shouldShow = (
props: {
editor: Editor;
node?: HTMLElement;
view?: EditorView;
state?: EditorState;
oldState?: EditorState;
from?: number;
to?: number;
},
bubbleMenu: NodeBubbleMenu
) => {
if (!props.editor.isEditable) {
return false;
}
return bubbleMenu.shouldShow?.(props);
};
</script>
<template>
<bubble-menu
v-for="(bubbleMenu, index) in getBubbleMenuFromExtensions()"
:key="index"
:plugin-key="bubbleMenu?.pluginKey"
:should-show="(prop) => shouldShow(prop, bubbleMenu)"
:editor="editor"
:tippy-options="{
maxWidth: '100%',
...bubbleMenu.tippyOptions,
}"
:get-render-container="bubbleMenu.getRenderContainer"
:default-animation="bubbleMenu.defaultAnimation"
>
<div
class="bubble-menu bg-white flex items-center rounded-md p-1 border drop-shadow space-x-0.5"
>
<template v-if="bubbleMenu.items">
<template
v-for="(item, itemIndex) in bubbleMenu.items"
:key="itemIndex"
>
<template v-if="item.component">
<component
:is="item.component"
v-bind="item.props"
:editor="editor"
/>
</template>
<bubble-item v-else :editor="editor" v-bind="item.props" />
</template>
</template>
<template v-else-if="bubbleMenu.component">
<component :is="bubbleMenu?.component" :editor="editor" />
</template>
</div>
</bubble-menu>
</template>
<style scoped>
.bubble-menu {
max-width: calc(100vw - 30px);
overflow-x: auto;
}
</style>