129 lines
3.3 KiB
TypeScript
129 lines
3.3 KiB
TypeScript
import { ReactRenderer } from '@tiptap/react'
|
|
import { MentionOptions } from '@tiptap/extension-mention'
|
|
import { SuggestionKeyDownProps, SuggestionProps } from '@tiptap/suggestion'
|
|
import tippy, { Instance as TippyInstance } from 'tippy.js'
|
|
import { getCookie } from 'cookies-next'
|
|
|
|
import {
|
|
MentionList,
|
|
MentionRef,
|
|
MentionSuggestion,
|
|
} from '~components/MentionList'
|
|
import api from '~utils/api'
|
|
import * as ElementTransformer from '~transformers/ElementTransformer'
|
|
import { get } from 'http'
|
|
|
|
interface RawSearchResponse {
|
|
searchable_type: string
|
|
granblueId: string
|
|
name_en: string
|
|
name_jp: string
|
|
element: number
|
|
}
|
|
|
|
interface SearchResponse {
|
|
name: {
|
|
[key: string]: string
|
|
en: string
|
|
ja: string
|
|
}
|
|
type: string
|
|
granblueId: string
|
|
element: GranblueElement
|
|
}
|
|
|
|
function transform(object: RawSearchResponse) {
|
|
const result: SearchResponse = {
|
|
name: {
|
|
en: object.name_en,
|
|
ja: object.name_jp,
|
|
},
|
|
type: object.searchable_type.toLowerCase(),
|
|
granblueId: object.granblueId,
|
|
element: ElementTransformer.toObject(object.element),
|
|
}
|
|
return result
|
|
}
|
|
|
|
export const mentionSuggestionOptions: MentionOptions['suggestion'] = {
|
|
items: async ({ query }): Promise<MentionSuggestion[]> => {
|
|
const locale = getCookie('NEXT_LOCALE')
|
|
? (getCookie('NEXT_LOCALE') as string)
|
|
: 'en'
|
|
const response = await api.searchAll(query, locale)
|
|
const results = response.data.results
|
|
|
|
return results
|
|
.map((rawObject: RawSearchResponse, index: number) => {
|
|
const object = transform(rawObject)
|
|
return {
|
|
granblueId: object.granblueId,
|
|
element: object.element,
|
|
type: object.type,
|
|
name: {
|
|
en: object.name.en,
|
|
ja: object.name.ja,
|
|
},
|
|
}
|
|
})
|
|
.slice(0, 7)
|
|
},
|
|
|
|
render: () => {
|
|
let component: ReactRenderer<MentionRef> | undefined
|
|
let popup: TippyInstance | undefined
|
|
|
|
return {
|
|
onStart: (props) => {
|
|
component = new ReactRenderer(MentionList, {
|
|
props,
|
|
editor: props.editor,
|
|
})
|
|
|
|
popup = tippy('body', {
|
|
getReferenceClientRect: props.clientRect,
|
|
appendTo: () => document.body,
|
|
content: component.element,
|
|
showOnCreate: true,
|
|
interactive: true,
|
|
trigger: 'manual',
|
|
placement: 'bottom-start',
|
|
})[0]
|
|
},
|
|
|
|
onUpdate(props) {
|
|
component?.updateProps(props)
|
|
|
|
popup?.setProps({
|
|
getReferenceClientRect: props.clientRect,
|
|
})
|
|
},
|
|
|
|
onKeyDown(props) {
|
|
if (props.event.key === 'Escape') {
|
|
popup?.hide()
|
|
return true
|
|
}
|
|
|
|
if (!component?.ref) {
|
|
return false
|
|
}
|
|
|
|
return component?.ref.onKeyDown(props)
|
|
},
|
|
|
|
onExit() {
|
|
popup?.destroy()
|
|
component?.destroy()
|
|
|
|
// Remove references to the old popup and component upon destruction/exit.
|
|
// (This should prevent redundant calls to `popup.destroy()`, which Tippy
|
|
// warns in the console is a sign of a memory leak, as the `suggestion`
|
|
// plugin seems to call `onExit` both when a suggestion menu is closed after
|
|
// a user chooses an option, *and* when the editor itself is destroyed.)
|
|
popup = undefined
|
|
component = undefined
|
|
},
|
|
}
|
|
},
|
|
}
|