You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1 line
				
				11 KiB
			
		
		
			
		
	
	
					1 line
				
				11 KiB
			| 
											3 years ago
										 | {"version":3,"file":"tiptap-extension-floating-menu.cjs","sources":["../src/floating-menu-plugin.ts","../src/floating-menu.ts"],"sourcesContent":["import { Editor, posToDOMRect } from '@tiptap/core'\nimport { EditorState, Plugin, PluginKey } from 'prosemirror-state'\nimport { EditorView } from 'prosemirror-view'\nimport tippy, { Instance, Props } from 'tippy.js'\n\nexport interface FloatingMenuPluginProps {\n  pluginKey: PluginKey | string,\n  editor: Editor,\n  element: HTMLElement,\n  tippyOptions?: Partial<Props>,\n  shouldShow?: ((props: {\n    editor: Editor,\n    view: EditorView,\n    state: EditorState,\n    oldState?: EditorState,\n  }) => boolean) | null,\n}\n\nexport type FloatingMenuViewProps = FloatingMenuPluginProps & {\n  view: EditorView,\n}\n\nexport class FloatingMenuView {\n  public editor: Editor\n\n  public element: HTMLElement\n\n  public view: EditorView\n\n  public preventHide = false\n\n  public tippy: Instance | undefined\n\n  public tippyOptions?: Partial<Props>\n\n  public shouldShow: Exclude<FloatingMenuPluginProps['shouldShow'], null> = ({ view, state }) => {\n    const { selection } = state\n    const { $anchor, empty } = selection\n    const isRootDepth = $anchor.depth === 1\n    const isEmptyTextBlock = $anchor.parent.isTextblock\n      && !$anchor.parent.type.spec.code\n      && !$anchor.parent.textContent\n\n    if (\n      !view.hasFocus()\n      || !empty\n      || !isRootDepth\n      || !isEmptyTextBlock\n      || !this.editor.isEditable\n    ) {\n      return false\n    }\n\n    return true\n  }\n\n  constructor({\n    editor,\n    element,\n    view,\n    tippyOptions = {},\n    shouldShow,\n  }: FloatingMenuViewProps) {\n    this.editor = editor\n    this.element = element\n    this.view = view\n\n    if (shouldShow) {\n      this.shouldShow = shouldShow\n    }\n\n    this.element.addEventListener('mousedown', this.mousedownHandler, { capture: true })\n    this.editor.on('focus', this.focusHandler)\n    this.editor.on('blur', this.blurHandler)\n    this.tippyOptions = tippyOptions\n    // Detaches menu content from its current parent\n    this.element.remove()\n    this.element.style.visibility = 'visible'\n  }\n\n  mousedownHandler = () => {\n    this.preventHide = true\n  }\n\n  focusHandler = () => {\n    // we use `setTimeout` to make sure `selection` is already updated\n    setTimeout(() => this.update(this.editor.view))\n  }\n\n  blurHandler = ({ event }: { event: FocusEvent }) => {\n    if (this.preventHide) {\n      this.preventHide = false\n\n      return\n    }\n\n    if (\n      event?.relatedTarget\n      && this.element.parentNode?.contains(event.relatedTarget as Node)\n    ) {\n      return\n    }\n\n    this.hide()\n  }\n\n  tippyBlurHandler = (event : FocusEvent) => {\n    this.blurHandler({ event })\n  }\n\n  createTooltip() {\n    const { element: editorElement } = this.editor.options\n    const editorIsAttached = !!editorElement.parentElement\n\n    if (this.tippy || !editorIsAttached) {\n      return\n    }\n\n    this.tippy = tippy(editorElement, {\n      duration: 0,\n      getReferenceClientRect: null,\n      content: this.element,\n      interactive: true,\n      trigger: 'manual',\n      placement: 'right',\n      hideOnClick: 'toggle',\n      ...this.tippyOptions,\n    })\n\n    // maybe we have to hide tippy on its own blur event as well\n    if (this.tippy.popper.firstChild) {\n      (this.tippy.popper.firstChild as HTMLElement).addEventListener('blur', this.tippyBlurHandler)\n    }\n  }\n\n  update(view: EditorView, oldState?: EditorState) {\n    const { state } = view\n    const { doc, selection } = state\n    const { from, to } = selection\n    const isSame = oldState && oldState.doc.eq(doc) && oldState.selection.eq(selection)\n\n    if (isSame) {\n      return\n    }\n\n    this.createTooltip()\n\n    const shouldShow = this.shouldShow?.({\n      editor: this.editor,\n      view,\n      state,\n      oldState,\n    })\n\n    if (!shouldShow) {\n      this.hide()\n\n      return\n    }\n\n    this.tippy?.setProps({\n      getReferenceClientRect: thi |