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.
90 lines
2.3 KiB
90 lines
2.3 KiB
import postEditor from '@/services/post-editor.service' |
|
import type { Editor } from '@tiptap/core' |
|
import tippy, { type GetReferenceClientRect, type Instance, type Props } from 'tippy.js' |
|
|
|
/** Above Radix Sheet/Dialog (`z-50`) so @-mention / emoji lists stay visible on mobile. */ |
|
export const SUGGESTION_POPUP_Z_INDEX = 350 |
|
|
|
export type SuggestionPopupController = { |
|
ensure: (props: { |
|
clientRect?: (() => DOMRect | null) | null |
|
content: Element |
|
}) => void |
|
hide: () => void |
|
destroy: () => void |
|
} |
|
|
|
export function createSuggestionPopup(editor: Editor): SuggestionPopupController { |
|
let popup: Instance | undefined |
|
let touchListener: ((e: TouchEvent) => void) | undefined |
|
|
|
const destroy = () => { |
|
if (touchListener) { |
|
document.removeEventListener('touchstart', touchListener) |
|
touchListener = undefined |
|
} |
|
if (popup) { |
|
popup.destroy() |
|
popup = undefined |
|
} |
|
postEditor.isSuggestionPopupOpen = false |
|
} |
|
|
|
const ensure = (props: { |
|
clientRect?: (() => DOMRect | null) | null |
|
content: Element |
|
}) => { |
|
if (!props.clientRect) return |
|
|
|
if (!touchListener) { |
|
touchListener = (e: TouchEvent) => { |
|
if (!popup || !postEditor.isSuggestionPopupOpen) return |
|
const target = e.target as Node |
|
if (popup.popper?.contains(target)) return |
|
const editorEl = editor.view?.dom |
|
if (editorEl?.contains(target)) return |
|
popup.hide() |
|
} |
|
document.addEventListener('touchstart', touchListener, { passive: true }) |
|
} |
|
|
|
const rectProps = { |
|
getReferenceClientRect: props.clientRect as GetReferenceClientRect |
|
} |
|
|
|
if (popup) { |
|
popup.setProps({ |
|
...rectProps, |
|
content: props.content |
|
} as Partial<Props>) |
|
if (!popup.state.isVisible) popup.show() |
|
return |
|
} |
|
|
|
popup = tippy(document.body, { |
|
...rectProps, |
|
appendTo: () => document.body, |
|
content: props.content, |
|
showOnCreate: true, |
|
interactive: true, |
|
trigger: 'manual', |
|
placement: 'bottom-start', |
|
hideOnClick: false, |
|
maxWidth: 'none', |
|
zIndex: SUGGESTION_POPUP_Z_INDEX, |
|
touch: true, |
|
onShow() { |
|
postEditor.isSuggestionPopupOpen = true |
|
}, |
|
onHide() { |
|
postEditor.isSuggestionPopupOpen = false |
|
} |
|
}) |
|
} |
|
|
|
return { |
|
ensure, |
|
hide: () => popup?.hide(), |
|
destroy |
|
} |
|
}
|
|
|