* add key option to bubble menu * ignore react for now * add shouldShow option to bubble menu extension * improve types * remove BubbleMenuPluginKey * add key and shouldShow option to floating menu extension * fix: don’t show floating menu within code block * docs: add new menu options
This commit is contained in:
@@ -9,9 +9,18 @@ import { EditorView } from 'prosemirror-view'
|
||||
import tippy, { Instance, Props } from 'tippy.js'
|
||||
|
||||
export interface BubbleMenuPluginProps {
|
||||
key: PluginKey | string,
|
||||
editor: Editor,
|
||||
element: HTMLElement,
|
||||
tippyOptions?: Partial<Props>,
|
||||
shouldShow: ((props: {
|
||||
editor: Editor,
|
||||
view: EditorView,
|
||||
state: EditorState,
|
||||
oldState?: EditorState,
|
||||
from: number,
|
||||
to: number,
|
||||
}) => boolean) | null,
|
||||
}
|
||||
|
||||
export type BubbleMenuViewProps = BubbleMenuPluginProps & {
|
||||
@@ -29,15 +38,38 @@ export class BubbleMenuView {
|
||||
|
||||
public tippy!: Instance
|
||||
|
||||
public shouldShow: Exclude<BubbleMenuPluginProps['shouldShow'], null> = ({ state, from, to }) => {
|
||||
const { doc, selection } = state
|
||||
const { empty } = selection
|
||||
|
||||
// Sometime check for `empty` is not enough.
|
||||
// Doubleclick an empty paragraph returns a node size of 2.
|
||||
// So we check also for an empty text size.
|
||||
const isEmptyTextBlock = !doc.textBetween(from, to).length
|
||||
&& isTextSelection(state.selection)
|
||||
|
||||
if (empty || isEmptyTextBlock) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
constructor({
|
||||
editor,
|
||||
element,
|
||||
view,
|
||||
tippyOptions,
|
||||
shouldShow,
|
||||
}: BubbleMenuViewProps) {
|
||||
this.editor = editor
|
||||
this.element = element
|
||||
this.view = view
|
||||
|
||||
if (shouldShow) {
|
||||
this.shouldShow = shouldShow
|
||||
}
|
||||
|
||||
this.element.addEventListener('mousedown', this.mousedownHandler, { capture: true })
|
||||
this.view.dom.addEventListener('dragstart', this.dragstartHandler)
|
||||
this.editor.on('focus', this.focusHandler)
|
||||
@@ -98,19 +130,21 @@ export class BubbleMenuView {
|
||||
return
|
||||
}
|
||||
|
||||
const { empty, ranges } = selection
|
||||
|
||||
// support for CellSelections
|
||||
const { ranges } = selection
|
||||
const from = Math.min(...ranges.map(range => range.$from.pos))
|
||||
const to = Math.max(...ranges.map(range => range.$to.pos))
|
||||
|
||||
// Sometime check for `empty` is not enough.
|
||||
// Doubleclick an empty paragraph returns a node size of 2.
|
||||
// So we check also for an empty text size.
|
||||
const isEmptyTextBlock = !doc.textBetween(from, to).length
|
||||
&& isTextSelection(view.state.selection)
|
||||
const shouldShow = this.shouldShow({
|
||||
editor: this.editor,
|
||||
view,
|
||||
state,
|
||||
oldState,
|
||||
from,
|
||||
to,
|
||||
})
|
||||
|
||||
if (empty || isEmptyTextBlock) {
|
||||
if (!shouldShow) {
|
||||
this.hide()
|
||||
|
||||
return
|
||||
@@ -118,7 +152,7 @@ export class BubbleMenuView {
|
||||
|
||||
this.tippy.setProps({
|
||||
getReferenceClientRect: () => {
|
||||
if (isNodeSelection(view.state.selection)) {
|
||||
if (isNodeSelection(state.selection)) {
|
||||
const node = view.nodeDOM(from) as HTMLElement
|
||||
|
||||
if (node) {
|
||||
@@ -150,11 +184,11 @@ export class BubbleMenuView {
|
||||
}
|
||||
}
|
||||
|
||||
export const BubbleMenuPluginKey = new PluginKey('menuBubble')
|
||||
|
||||
export const BubbleMenuPlugin = (options: BubbleMenuPluginProps) => {
|
||||
return new Plugin({
|
||||
key: BubbleMenuPluginKey,
|
||||
key: typeof options.key === 'string'
|
||||
? new PluginKey(options.key)
|
||||
: options.key,
|
||||
view: view => new BubbleMenuView({ view, ...options }),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ export const BubbleMenu = Extension.create<BubbleMenuOptions>({
|
||||
defaultOptions: {
|
||||
element: null,
|
||||
tippyOptions: {},
|
||||
key: 'bubbleMenu',
|
||||
shouldShow: null,
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
@@ -20,9 +22,11 @@ export const BubbleMenu = Extension.create<BubbleMenuOptions>({
|
||||
|
||||
return [
|
||||
BubbleMenuPlugin({
|
||||
key: this.options.key,
|
||||
editor: this.editor,
|
||||
element: this.options.element,
|
||||
tippyOptions: this.options.tippyOptions,
|
||||
shouldShow: this.options.shouldShow,
|
||||
}),
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user