mirror of
https://github.com/plausible/analytics.git
synced 2024-12-25 10:33:01 +03:00
97b24c0492
* Add SSO link with signed JWT token * Falls back to Nolt URL without SSO if token cannot be generated * Add profile image (gravatar) to Nolt SSO link * Improve navbar dropdown * Add 'contact support' link to nav dropdown * Add CSS rule to prevent horizontal jumps * Dark mode styling * Close dropdown when link is clicked * Clarify links in dropdown * Clarify CSS comment * Use Alpine.data() over window * Rename suggestions_dropdown -> combo-box * Mix format * Make logout link look good on dark mode * Use proxy for gravatar * Do not use Gravatar proxy in self-hosted * Changelog * Add Github Repo link to nav dropdown * Make dialyzer happy * Add proxy for Gravatar * Update assets/css/app.css Co-authored-by: hq1 <hq@mtod.org> * Update lib/plausible_web/controllers/avatar_controller.ex Co-authored-by: hq1 <hq@mtod.org> * Fix alpine <> Liveview integration --------- Co-authored-by: hq1 <hq@mtod.org>
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)
|
|
}
|
|
})
|