54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
![]() |
import { renderGrouped } from 'prosemirror-menu'
|
||
|
import { EditorState, Plugin } from 'prosemirror-state'
|
||
|
import styles from '../styles/ProseMirror.module.scss'
|
||
|
import type { EditorView } from 'prosemirror-view'
|
||
|
import type { DiscoursSchema } from '../schema'
|
||
|
import { buildMenuItems } from '../helpers/menu'
|
||
|
|
||
|
export class SelectionMenuView {
|
||
|
tooltip: HTMLDivElement
|
||
|
|
||
|
constructor(view: EditorView, schema: DiscoursSchema) {
|
||
|
this.tooltip = document.createElement('div')
|
||
|
this.tooltip.className = styles.selectionMenu
|
||
|
view.dom.parentNode.appendChild(this.tooltip)
|
||
|
const { dom } = renderGrouped(view, buildMenuItems(schema))
|
||
|
this.tooltip.appendChild(dom)
|
||
|
this.update(view, null)
|
||
|
}
|
||
|
|
||
|
update(view: EditorView, lastState: EditorState) {
|
||
|
const state = view.state
|
||
|
|
||
|
if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if (state.selection.empty) {
|
||
|
this.tooltip.style.display = 'none'
|
||
|
return
|
||
|
}
|
||
|
|
||
|
this.tooltip.style.display = ''
|
||
|
const { from, to } = state.selection
|
||
|
const start = view.coordsAtPos(from)
|
||
|
const end = view.coordsAtPos(to)
|
||
|
const box = this.tooltip.offsetParent.getBoundingClientRect()
|
||
|
const width = this.tooltip.getBoundingClientRect().width
|
||
|
const left = (start.left + end.left - width) / 2
|
||
|
this.tooltip.style.left = `${left - box.left}px`
|
||
|
this.tooltip.style.bottom = `${box.bottom - start.top + 8}px`
|
||
|
}
|
||
|
|
||
|
destroy() {
|
||
|
this.tooltip.remove()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export const selectionMenu = (schema: DiscoursSchema) =>
|
||
|
new Plugin({
|
||
|
view(editorView: EditorView) {
|
||
|
return new SelectionMenuView(editorView, schema)
|
||
|
}
|
||
|
})
|