X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/8f3430d3867ca8880c4f259b2c58647eb2b2c5ea..refs/pull/5626/head:/resources/js/components/auto-suggest.js diff --git a/resources/js/components/auto-suggest.js b/resources/js/components/auto-suggest.js index 80857cbe5..0b828e71b 100644 --- a/resources/js/components/auto-suggest.js +++ b/resources/js/components/auto-suggest.js @@ -1,13 +1,15 @@ -import {escapeHtml} from "../services/util"; -import {onChildEvent} from "../services/dom"; +import {escapeHtml} from '../services/util.ts'; +import {onChildEvent} from '../services/dom.ts'; +import {Component} from './component'; +import {KeyboardNavigationHandler} from '../services/keyboard-navigation.ts'; const ajaxCache = {}; /** * AutoSuggest - * @extends {Component} */ -class AutoSuggest { +export class AutoSuggest extends Component { + setup() { this.parent = this.$el.parentElement; this.container = this.$el; @@ -21,26 +23,34 @@ class AutoSuggest { } setupListeners() { + const navHandler = new KeyboardNavigationHandler( + this.list, + () => { + this.input.focus(); + setTimeout(() => this.hideSuggestions(), 1); + }, + event => { + event.preventDefault(); + const selectionValue = event.target.textContent; + if (selectionValue) { + this.selectSuggestion(selectionValue); + } + }, + ); + navHandler.shareHandlingToEl(this.input); + + onChildEvent(this.list, '.text-item', 'click', (event, el) => { + this.selectSuggestion(el.textContent); + }); + this.input.addEventListener('input', this.requestSuggestions.bind(this)); this.input.addEventListener('focus', this.requestSuggestions.bind(this)); + this.input.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this)); this.input.addEventListener('keydown', event => { if (event.key === 'Tab') { this.hideSuggestions(); } }); - - this.input.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this)); - this.container.addEventListener('keydown', this.containerKeyDown.bind(this)); - - onChildEvent(this.list, 'button', 'click', (event, el) => { - this.selectSuggestion(el.textContent); - }); - onChildEvent(this.list, 'button', 'keydown', (event, el) => { - if (event.key === 'Enter') { - this.selectSuggestion(el.textContent); - } - }); - } selectSuggestion(value) { @@ -52,36 +62,6 @@ class AutoSuggest { this.hideSuggestions(); } - containerKeyDown(event) { - if (event.key === 'Enter') event.preventDefault(); - if (this.list.classList.contains('hidden')) return; - - // Down arrow - if (event.key === 'ArrowDown') { - this.moveFocus(true); - event.preventDefault(); - } - // Up Arrow - else if (event.key === 'ArrowUp') { - this.moveFocus(false); - event.preventDefault(); - } - // Escape key - else if (event.key === 'Escape') { - this.hideSuggestions(); - event.preventDefault(); - } - } - - moveFocus(forward = true) { - const focusables = Array.from(this.container.querySelectorAll('input,button')); - const index = focusables.indexOf(document.activeElement); - const newFocus = focusables[index + (forward ? 1 : -1)]; - if (newFocus) { - newFocus.focus() - } - } - async requestSuggestions() { if (Date.now() - this.lastPopulated < 50) { return; @@ -91,9 +71,7 @@ class AutoSuggest { const search = this.input.value.toLowerCase(); const suggestions = await this.loadSuggestions(search, nameFilter); - const toShow = suggestions.filter(val => { - return search === '' || val.toLowerCase().startsWith(search); - }).slice(0, 10); + const toShow = suggestions.filter(val => search === '' || val.toLowerCase().startsWith(search)).slice(0, 10); this.displaySuggestions(toShow); } @@ -129,12 +107,15 @@ class AutoSuggest { */ displaySuggestions(suggestions) { if (suggestions.length === 0) { - return this.hideSuggestions(); + this.hideSuggestions(); + return; } - this.list.innerHTML = suggestions.map(value => `
`).join(''); + // This used to use