Files
once-campfire/app/javascript/lib/autocomplete/base_autocomplete_handler.js
Kevin McConnell df76a227dc Hello world
First open source release of Campfire 🎉
2025-08-21 09:31:59 +01:00

129 lines
3.2 KiB
JavaScript

import Collection from "lib/autocomplete/collection"
import SuggestionController from "lib/autocomplete/suggestion_controller"
import { generateUUID } from "lib/autocomplete/helpers"
import { Renderer } from "lib/autocomplete/renderer"
export default class BaseAutocompleteHandler {
#autocompletables
#url
constructor(element, url) {
this.element = element
this.#url = url
if (!this.element.id) { this.element.id = `autocomplete_${generateUUID()}` }
this.suggestionController = new SuggestionController(this)
}
updateWithContentAndPosition(content, position) {
if (this.suggestionController && this.shouldAutocompleteWithContentAndPosition(content, position)) {
this.suggestionController.updateWithContentAndPosition(content, position)
}
}
destroy() {
this.#closeSuggestionController()
}
// Subclass methods
get pattern() {
return null
}
shouldAutocompleteWithContentAndPosition(content, position) {
return true
}
getAutocompletable(value) {
return this.#autocompletables.get(value)
}
autocompletablesMatchingQuery(query) {
return this.#autocompletables.matchingQuery(query).toArray()
}
loadAutocompletables(query, callback) {
const url = query ? this.#autocompletablesUrl(query) : this.#url
this.#fetchAutocompletables(url).then((autocompletables) => {
this.setAutocompletables(autocompletables)
callback()
})
}
setAutocompletables(autocompletables) {
this.#autocompletables = new Collection(autocompletables)
}
// SuggestionController Delegate
getSuggestionsIdentifier() {
return `${this.element.id}_suggestions`
}
matchQueryAndTerminatorForWord(word) {
if (!this.pattern) return
const match = word.match(this.pattern)
if (match) {
return {
query: match[1],
terminator: match?.[2] || ""
}
}
}
getOffsetsAtPosition(position) {
return this.element.getBoundingClientRect()
}
getResultsPlacement() {
return this.#suggestionResultsPlacement
}
fetchResultsForQuery(query, callback) {
this.loadAutocompletables(query, () => {
const autocompletables = this.autocompletablesMatchingQuery(query)
const html = new Renderer().renderAutocompletableSuggestions(autocompletables)
callback(html)
})
}
willCommitValueAtRangeWithTerminator(value, range, terminator) {
const autocompletable = this.getAutocompletable(value)
this.insertAutocompletable(autocompletable, range, terminator)
}
didShowResults(selectElement) {
selectElement.classList.add("rich_text")
}
#autocompletablesUrl(query) {
const separator = this.#url.includes('?') ? '&' : '?'
return `${this.#url}${separator}query=${query}`
}
get #suggestionResultsPlacement() {
return this.element.dataset.suggestionResultsPlacement
}
#closeSuggestionController() {
if (!this.suggestionController) return
if (this.suggestionController.active) {
this.suggestionController.hideResults()
} else {
this.suggestionController.destroy()
this.suggestionController = null
}
}
#fetchAutocompletables(url) {
if (url) {
return fetch(url, { as: "json" }).then(response => response.json())
} else {
return Promise.resolve()
}
}
}