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

135 lines
4.1 KiB
JavaScript

import { camelize, normalize, regexpForQuery, uniqueValues } from "lib/autocomplete/utils"
export default class AutocompletableCollection {
#autocompletables
#index
constructor(autocompletables = [], options = {}) {
this.#index = new Map()
this.#autocompletables = new Array()
Array.from(autocompletables).forEach((autocompletable) => {
this.#index.set(this.#uniqueAutocompleteableKey(autocompletable), autocompletable)
})
this.#index.forEach(autocompletable => {
this.#autocompletables.push(autocompletable)
})
if (options.sort !== false) {
this.#autocompletables.sort(this.#compareAutocompletables)
}
}
get(value) {
return this.#index.get(value.toString())
}
has(value) {
return this.#index.has(value.toString())
}
add(autocompletables = [], collectionOptions) {
return new this.constructor(this.#autocompletables.concat(autocompletables), collectionOptions)
}
getValues() {
return this.#autocompletables.map(autocompletable => autocompletable.value)
}
withValues(values = [], collectionOptions) {
const autocompletables = values.map((value) => this.get(value)).filter(Boolean)
return new this.constructor(autocompletables, collectionOptions)
}
withoutValues(values = [], collectionOptions) {
const autocompletables = []
this.#index.forEach(function(autocompletable, value) {
const allGroupMembersAreAdded = autocompletable.type == "group" && autocompletable.value.split(",").every(id => values.includes(id))
if (!values.includes(value) && !allGroupMembersAreAdded) {
return autocompletables.push(autocompletable)
}
})
return new this.constructor(autocompletables, collectionOptions)
}
filter(callback, collectionOptions) {
if (!callback) { return this }
const autocompletables = []
this.#index.forEach(function(autocompletable, value) {
if (callback(autocompletable)) {
return autocompletables.push(autocompletable)
}
})
return new this.constructor(autocompletables, collectionOptions)
}
matchingQuery(query) {
if (!query) { return this }
return new this.constructor(
this.#matchAutocompletablesByNameOrDescription(this.#autocompletables, query),
{ sort: false }
)
}
toArray() {
return this.#autocompletables.slice(0)
}
toJSON() {
return this.toArray()
}
isEqualTo(collection) {
if (!collection || (this.#autocompletables.length !== collection.length)) {
return false
}
return JSON.stringify(this) === JSON.stringify(collection)
}
#compareAutocompletables(autocompletable, otherAutocompletable) {
return autocompletable.name.localeCompare(otherAutocompletable.name)
}
#matchAutocompletablesByNameOrDescription(autocompletables, query) {
return uniqueValues([].concat(
this.#matchAutocompletablesByNameAtHead(autocompletables, query),
this.#matchAutocompletablesByRestOfName(autocompletables, query),
this.#matchAutocompletablesByRestOfDescription(autocompletables, query))
)
}
#matchAutocompletablesByNameAtHead(autocompletables, query) {
return this.#matchAutocompletablesByRegExp(autocompletables, regexpForQuery(query, "^"))
}
#matchAutocompletablesByRestOfName(autocompletables, query) {
return this.#matchAutocompletablesByRegExp(autocompletables, regexpForQuery(query, "\\s"))
}
#matchAutocompletablesByRestOfDescription(autocompletables, query) {
return this.#matchAutocompletablesByRegExp(autocompletables, regexpForQuery("", query), "description")
}
#matchAutocompletablesByRegExp(autocompletables, regexp, propertyName = "name") {
return autocompletables.filter(autocompletable => {
const normalizedPropertyName = `normalized${camelize(propertyName)}`
const property = autocompletable[propertyName]
if (property) {
if (!autocompletable[normalizedPropertyName]) autocompletable[normalizedPropertyName] = normalize(property)
return regexp.test(autocompletable[normalizedPropertyName])
}
})
}
#uniqueAutocompleteableKey(autocompletable) {
return autocompletable.value.toString()
}
}