mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
place tippy popup in renderless component
This commit is contained in:
parent
fbe4de347b
commit
2c0b2ed482
52
webapp/components/Editor/ContextMenu.vue
Normal file
52
webapp/components/Editor/ContextMenu.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<script>
|
||||||
|
import tippy from 'tippy.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
content: Object,
|
||||||
|
node: Object,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
displayContextMenu(target, content) {
|
||||||
|
if (this.menu) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.menu = tippy(target, {
|
||||||
|
content: content,
|
||||||
|
trigger: 'mouseenter',
|
||||||
|
interactive: true,
|
||||||
|
theme: 'dark',
|
||||||
|
placement: 'top-start',
|
||||||
|
inertia: true,
|
||||||
|
duration: [400, 200],
|
||||||
|
showOnInit: true,
|
||||||
|
arrow: true,
|
||||||
|
arrowType: 'round',
|
||||||
|
})
|
||||||
|
// we have to update tippy whenever the DOM is updated
|
||||||
|
if (MutationObserver) {
|
||||||
|
this.observer = new MutationObserver(() => {
|
||||||
|
this.menu.popperInstance.scheduleUpdate()
|
||||||
|
})
|
||||||
|
this.observer.observe(content, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
characterData: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideContextMenu() {
|
||||||
|
if (this.menu) {
|
||||||
|
this.menu.destroy()
|
||||||
|
this.menu = null
|
||||||
|
}
|
||||||
|
if (this.observer) {
|
||||||
|
this.observer.disconnect()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -48,6 +48,7 @@
|
|||||||
</editor-menu-bubble>
|
</editor-menu-bubble>
|
||||||
<menu-bar :editor="editor" :showLinkMenu="showLinkMenu" />
|
<menu-bar :editor="editor" :showLinkMenu="showLinkMenu" />
|
||||||
<editor-content ref="editor" :editor="editor" />
|
<editor-content ref="editor" :editor="editor" />
|
||||||
|
<context-menu ref="contextMenu" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -56,7 +57,6 @@ import defaultExtensions from './defaultExtensions.js'
|
|||||||
import linkify from 'linkify-it'
|
import linkify from 'linkify-it'
|
||||||
import stringHash from 'string-hash'
|
import stringHash from 'string-hash'
|
||||||
import Fuse from 'fuse.js'
|
import Fuse from 'fuse.js'
|
||||||
import tippy from 'tippy.js'
|
|
||||||
import { Editor, EditorContent, EditorMenuBubble } from 'tiptap'
|
import { Editor, EditorContent, EditorMenuBubble } from 'tiptap'
|
||||||
import EventHandler from './plugins/eventHandler.js'
|
import EventHandler from './plugins/eventHandler.js'
|
||||||
import { History } from 'tiptap-extensions'
|
import { History } from 'tiptap-extensions'
|
||||||
@ -65,6 +65,7 @@ import Mention from './nodes/Mention.js'
|
|||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import MenuBar from './MenuBar'
|
import MenuBar from './MenuBar'
|
||||||
import SuggestionsMenu from './SuggestionsMenu'
|
import SuggestionsMenu from './SuggestionsMenu'
|
||||||
|
import ContextMenu from './ContextMenu'
|
||||||
|
|
||||||
let throttleInputEvent
|
let throttleInputEvent
|
||||||
|
|
||||||
@ -74,6 +75,7 @@ export default {
|
|||||||
EditorMenuBubble,
|
EditorMenuBubble,
|
||||||
MenuBar,
|
MenuBar,
|
||||||
SuggestionsMenu,
|
SuggestionsMenu,
|
||||||
|
ContextMenu,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
users: { type: Array, default: () => null }, // If 'null', than the Mention extention is not assigned.
|
users: { type: Array, default: () => null }, // If 'null', than the Mention extention is not assigned.
|
||||||
@ -99,7 +101,7 @@ export default {
|
|||||||
this.query = query
|
this.query = query
|
||||||
this.filteredItems = items
|
this.filteredItems = items
|
||||||
this.suggestionRange = range
|
this.suggestionRange = range
|
||||||
this.renderPopup(virtualNode)
|
this.$refs.contextMenu.displayContextMenu(virtualNode, this.$refs.suggestions.$el)
|
||||||
// we save the command for inserting a selected mention
|
// we save the command for inserting a selected mention
|
||||||
// this allows us to call it inside of our custom popup
|
// this allows us to call it inside of our custom popup
|
||||||
// via keyboard navigation and on click
|
// via keyboard navigation and on click
|
||||||
@ -111,7 +113,7 @@ export default {
|
|||||||
this.filteredItems = items
|
this.filteredItems = items
|
||||||
this.suggestionRange = range
|
this.suggestionRange = range
|
||||||
this.navigatedItemIndex = 0
|
this.navigatedItemIndex = 0
|
||||||
this.renderPopup(virtualNode)
|
this.$refs.contextMenu.displayContextMenu(virtualNode, this.$refs.suggestions.$el)
|
||||||
},
|
},
|
||||||
// is called when a suggestion is cancelled
|
// is called when a suggestion is cancelled
|
||||||
onExit: () => {
|
onExit: () => {
|
||||||
@ -122,7 +124,7 @@ export default {
|
|||||||
this.filteredItems = []
|
this.filteredItems = []
|
||||||
this.suggestionRange = null
|
this.suggestionRange = null
|
||||||
this.navigatedItemIndex = 0
|
this.navigatedItemIndex = 0
|
||||||
this.destroyPopup()
|
this.$refs.contextMenu.hideContextMenu()
|
||||||
},
|
},
|
||||||
// is called on every keyDown event while a suggestion is active
|
// is called on every keyDown event while a suggestion is active
|
||||||
onKeyDown: ({ event }) => {
|
onKeyDown: ({ event }) => {
|
||||||
@ -175,7 +177,7 @@ export default {
|
|||||||
this.query = this.sanitizedQuery(query)
|
this.query = this.sanitizedQuery(query)
|
||||||
this.filteredItems = items
|
this.filteredItems = items
|
||||||
this.suggestionRange = range
|
this.suggestionRange = range
|
||||||
this.renderPopup(virtualNode)
|
this.$refs.contextMenu.displayContextMenu(virtualNode, this.$refs.suggestions.$el)
|
||||||
// we save the command for inserting a selected mention
|
// we save the command for inserting a selected mention
|
||||||
// this allows us to call it inside of our custom popup
|
// this allows us to call it inside of our custom popup
|
||||||
// via keyboard navigation and on click
|
// via keyboard navigation and on click
|
||||||
@ -187,7 +189,7 @@ export default {
|
|||||||
this.filteredItems = items
|
this.filteredItems = items
|
||||||
this.suggestionRange = range
|
this.suggestionRange = range
|
||||||
this.navigatedItemIndex = 0
|
this.navigatedItemIndex = 0
|
||||||
this.renderPopup(virtualNode)
|
this.$refs.contextMenu.displayContextMenu(virtualNode, this.$refs.suggestions.$el)
|
||||||
},
|
},
|
||||||
// is called when a suggestion is cancelled
|
// is called when a suggestion is cancelled
|
||||||
onExit: () => {
|
onExit: () => {
|
||||||
@ -198,7 +200,7 @@ export default {
|
|||||||
this.filteredItems = []
|
this.filteredItems = []
|
||||||
this.suggestionRange = null
|
this.suggestionRange = null
|
||||||
this.navigatedItemIndex = 0
|
this.navigatedItemIndex = 0
|
||||||
this.destroyPopup()
|
this.$refs.contextMenu.hideContextMenu()
|
||||||
},
|
},
|
||||||
// is called on every keyDown event while a suggestion is active
|
// is called on every keyDown event while a suggestion is active
|
||||||
onKeyDown: ({ event }) => {
|
onKeyDown: ({ event }) => {
|
||||||
@ -363,45 +365,6 @@ export default {
|
|||||||
})
|
})
|
||||||
this.editor.focus()
|
this.editor.focus()
|
||||||
},
|
},
|
||||||
// renders a popup with suggestions
|
|
||||||
// tiptap provides a virtualNode object for using popper.js (or tippy.js) for popups
|
|
||||||
renderPopup(node) {
|
|
||||||
if (this.popup) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.popup = tippy(node, {
|
|
||||||
content: this.$refs.suggestions.$el,
|
|
||||||
trigger: 'mouseenter',
|
|
||||||
interactive: true,
|
|
||||||
theme: 'dark',
|
|
||||||
placement: 'top-start',
|
|
||||||
inertia: true,
|
|
||||||
duration: [400, 200],
|
|
||||||
showOnInit: true,
|
|
||||||
arrow: true,
|
|
||||||
arrowType: 'round',
|
|
||||||
})
|
|
||||||
// we have to update tippy whenever the DOM is updated
|
|
||||||
if (MutationObserver) {
|
|
||||||
this.observer = new MutationObserver(() => {
|
|
||||||
this.popup.popperInstance.scheduleUpdate()
|
|
||||||
})
|
|
||||||
this.observer.observe(this.$refs.suggestions.$el, {
|
|
||||||
childList: true,
|
|
||||||
subtree: true,
|
|
||||||
characterData: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
destroyPopup() {
|
|
||||||
if (this.popup) {
|
|
||||||
this.popup.destroy()
|
|
||||||
this.popup = null
|
|
||||||
}
|
|
||||||
if (this.observer) {
|
|
||||||
this.observer.disconnect()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onUpdate(e) {
|
onUpdate(e) {
|
||||||
const content = e.getHTML()
|
const content = e.getHTML()
|
||||||
const contentHash = stringHash(content)
|
const contentHash = stringHash(content)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user