mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Fixed click issue with tag suggestions in safari
Updated selectable elements to be divs instead of buttons since Safari akwardly does not focus on buttons on click. Also standardised keyboard handling to our standard nav class. Also addressed empty tag values showing in results. For #4139
This commit is contained in:
parent
fd674d10e3
commit
e722ee4268
@ -11,11 +11,9 @@ use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TagRepo
|
||||
{
|
||||
protected PermissionApplicator $permissions;
|
||||
|
||||
public function __construct(PermissionApplicator $permissions)
|
||||
{
|
||||
$this->permissions = $permissions;
|
||||
public function __construct(
|
||||
protected PermissionApplicator $permissions
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,6 +88,7 @@ class TagRepo
|
||||
{
|
||||
$query = Tag::query()
|
||||
->select('*', DB::raw('count(*) as count'))
|
||||
->where('value', '!=', '')
|
||||
->groupBy('value');
|
||||
|
||||
if ($searchTerm) {
|
||||
|
@ -8,11 +8,9 @@ use Illuminate\Http\Request;
|
||||
|
||||
class TagController extends Controller
|
||||
{
|
||||
protected TagRepo $tagRepo;
|
||||
|
||||
public function __construct(TagRepo $tagRepo)
|
||||
{
|
||||
$this->tagRepo = $tagRepo;
|
||||
public function __construct(
|
||||
protected TagRepo $tagRepo
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {escapeHtml} from "../services/util";
|
||||
import {onChildEvent} from "../services/dom";
|
||||
import {Component} from "./component";
|
||||
import {KeyboardNavigationHandler} from "../services/keyboard-navigation";
|
||||
|
||||
const ajaxCache = {};
|
||||
|
||||
@ -21,26 +22,31 @@ export class AutoSuggest extends Component {
|
||||
}
|
||||
|
||||
setupListeners() {
|
||||
const navHandler = new KeyboardNavigationHandler(
|
||||
this.list,
|
||||
event => {
|
||||
this.input.focus();
|
||||
setTimeout(() => this.hideSuggestions(), 1);
|
||||
},
|
||||
event => {
|
||||
event.preventDefault();
|
||||
this.selectSuggestion(event.target.textContent);
|
||||
},
|
||||
);
|
||||
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 +58,6 @@ export class AutoSuggest extends Component {
|
||||
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;
|
||||
@ -132,9 +108,11 @@ export class AutoSuggest extends Component {
|
||||
return this.hideSuggestions();
|
||||
}
|
||||
|
||||
this.list.innerHTML = suggestions.map(value => `<li><button type="button" class="text-item">${escapeHtml(value)}</button></li>`).join('');
|
||||
// This used to use <button>s but was changed to div elements since Safari would not focus on buttons
|
||||
// on which causes a range of other complexities related to focus handling.
|
||||
this.list.innerHTML = suggestions.map(value => `<li><div tabindex="-1" class="text-item">${escapeHtml(value)}</div></li>`).join('');
|
||||
this.list.style.display = 'block';
|
||||
for (const button of this.list.querySelectorAll('button')) {
|
||||
for (const button of this.list.querySelectorAll('.text-item')) {
|
||||
button.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user