111 lines
2.5 KiB
JavaScript
111 lines
2.5 KiB
JavaScript
|
import { computePosition, flip, shift } from '@floating-ui/dom'
|
||
|
import { posToDOMRect, VueRenderer } from '@tiptap/vue-3'
|
||
|
|
||
|
import MentionList from './MentionList.vue'
|
||
|
import { defAvatar } from '@/constant/default'
|
||
|
|
||
|
const updatePosition = (editor, element) => {
|
||
|
const virtualElement = {
|
||
|
getBoundingClientRect: () => posToDOMRect(editor.view, editor.state.selection.from, editor.state.selection.to),
|
||
|
}
|
||
|
|
||
|
computePosition(virtualElement, element, {
|
||
|
placement: 'bottom-start',
|
||
|
strategy: 'absolute',
|
||
|
middleware: [shift(), flip()],
|
||
|
}).then(({ x, y, strategy }) => {
|
||
|
element.style.position = strategy
|
||
|
if (window.__POWERED_BY_WUJIE__) {
|
||
|
element.style.left = `${x + 200}px`
|
||
|
element.style.top = `${y + 100}px`
|
||
|
} else {
|
||
|
element.style.left = `${x}px`
|
||
|
element.style.top = `${y}px`
|
||
|
}
|
||
|
|
||
|
|
||
|
})
|
||
|
}
|
||
|
|
||
|
export default {
|
||
|
items: ({ query, editor, props }) => {
|
||
|
if (!props.members || !props.members.length) {
|
||
|
return []
|
||
|
}
|
||
|
|
||
|
let list = [...props.members]
|
||
|
|
||
|
// 如果是群组管理员,添加"所有人"选项
|
||
|
if (props.isGroupManager) {
|
||
|
list.unshift({ id: 0, nickname: '所有人', avatar: defAvatar })
|
||
|
}
|
||
|
|
||
|
const filteredItems = list.filter(
|
||
|
(item) => item.nickname.toLowerCase().includes(query.toLowerCase())
|
||
|
)
|
||
|
|
||
|
// 如果没有匹配项,返回空数组以关闭弹窗
|
||
|
if (filteredItems.length === 0) {
|
||
|
return []
|
||
|
}
|
||
|
|
||
|
return filteredItems
|
||
|
},
|
||
|
|
||
|
render: () => {
|
||
|
let component
|
||
|
|
||
|
return {
|
||
|
onStart: props => {
|
||
|
// 如果没有匹配项,不创建弹窗
|
||
|
if (!props.items || props.items.length === 0) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
component = new VueRenderer(MentionList, {
|
||
|
// Vue 3 props格式
|
||
|
props,
|
||
|
editor: props.editor,
|
||
|
})
|
||
|
|
||
|
if (!props.clientRect) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
component.element.style.position = 'absolute'
|
||
|
|
||
|
document.body.appendChild(component.element)
|
||
|
|
||
|
updatePosition(props.editor, component.element)
|
||
|
},
|
||
|
|
||
|
onUpdate(props) {
|
||
|
component.updateProps(props)
|
||
|
|
||
|
if (props.items.length === 0) {
|
||
|
this.onExit()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (!props.clientRect) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
updatePosition(props.editor, component.element)
|
||
|
},
|
||
|
|
||
|
onKeyDown(props) {
|
||
|
if (props.event.key === 'Escape') {
|
||
|
this.onExit()
|
||
|
return true
|
||
|
}
|
||
|
return component.ref.onKeyDown(props)
|
||
|
},
|
||
|
|
||
|
onExit() {
|
||
|
component.element.remove()
|
||
|
component.destroy()
|
||
|
},
|
||
|
}
|
||
|
},
|
||
|
}
|