mirror of
https://github.com/plausible/analytics.git
synced 2024-12-26 02:55:02 +03:00
78 lines
2.0 KiB
JavaScript
78 lines
2.0 KiB
JavaScript
|
// Courtesy of Benjamin von Polheim:
|
||
|
// https://blog.devgenius.io/build-a-performat-autocomplete-using-phoenix-liveview-and-alpine-js-8bcbbed17ba7
|
||
|
|
||
|
export default (id) => ({
|
||
|
isOpen: false,
|
||
|
id: id,
|
||
|
focus: null,
|
||
|
setFocus(f) {
|
||
|
this.focus = f;
|
||
|
},
|
||
|
initFocus() {
|
||
|
if (this.focus === null) {
|
||
|
this.setFocus(this.leastFocusableIndex())
|
||
|
}
|
||
|
},
|
||
|
open() {
|
||
|
this.initFocus()
|
||
|
this.isOpen = true
|
||
|
},
|
||
|
suggestionsCount() {
|
||
|
return this.$refs.suggestions?.querySelectorAll('li').length
|
||
|
},
|
||
|
hasCreatableOption() {
|
||
|
return this.$refs.suggestions?.querySelector('li').classList.contains("creatable")
|
||
|
},
|
||
|
leastFocusableIndex() {
|
||
|
if (this.suggestionsCount() === 0) {
|
||
|
return 0
|
||
|
}
|
||
|
return this.hasCreatableOption() ? 0 : 1
|
||
|
},
|
||
|
maxFocusableIndex() {
|
||
|
return this.hasCreatableOption() ? this.suggestionsCount() - 1 : this.suggestionsCount()
|
||
|
},
|
||
|
nextFocusableIndex() {
|
||
|
const currentFocus = this.focus
|
||
|
return currentFocus + 1 > this.maxFocusableIndex() ? this.leastFocusableIndex() : currentFocus + 1
|
||
|
},
|
||
|
prevFocusableIndex() {
|
||
|
const currentFocus = this.focus
|
||
|
return currentFocus - 1 >= this.leastFocusableIndex() ? currentFocus - 1 : this.maxFocusableIndex()
|
||
|
},
|
||
|
close(e) {
|
||
|
// Pressing Escape should not propagate to window,
|
||
|
// so we'll only close the suggestions pop-up
|
||
|
if (this.isOpen && e.key === "Escape") {
|
||
|
e.stopPropagation()
|
||
|
}
|
||
|
this.isOpen = false
|
||
|
},
|
||
|
select() {
|
||
|
this.$refs[`dropdown-${this.id}-option-${this.focus}`]?.click()
|
||
|
this.close()
|
||
|
document.getElementById(this.id).blur()
|
||
|
},
|
||
|
scrollTo(idx) {
|
||
|
this.$refs[`dropdown-${this.id}-option-${idx}`]?.scrollIntoView(
|
||
|
{ block: 'nearest', behavior: 'smooth', inline: 'start' }
|
||
|
)
|
||
|
},
|
||
|
focusNext() {
|
||
|
const nextIndex = this.nextFocusableIndex()
|
||
|
|
||
|
if (!this.isOpen) this.open()
|
||
|
|
||
|
this.setFocus(nextIndex)
|
||
|
this.scrollTo(nextIndex)
|
||
|
},
|
||
|
focusPrev() {
|
||
|
const prevIndex = this.prevFocusableIndex()
|
||
|
|
||
|
if (!this.isOpen) this.open()
|
||
|
|
||
|
this.setFocus(prevIndex)
|
||
|
this.scrollTo(prevIndex)
|
||
|
}
|
||
|
})
|