mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-12-11 12:47:54 +08:00
cf5d51e7b8
- Set proper semantic tags for main parts of content. - Removed focus-trap from tag manager/autosuggest. - Set better accessibility labelling on tag manager. - Updated collapsible sections to be keyboard navigatable. - Improved input focus styling to better fit theme. - Updated custom styled file picker to be accessible via keyboard. Related to #1320
131 lines
3.8 KiB
JavaScript
131 lines
3.8 KiB
JavaScript
|
|
const template = `
|
|
<div>
|
|
<input :value="value" :autosuggest-type="type" ref="input"
|
|
:placeholder="placeholder" :name="name"
|
|
@input="inputUpdate($event.target.value)" @focus="inputUpdate($event.target.value)"
|
|
@blur="inputBlur"
|
|
@keydown="inputKeydown"
|
|
:aria-label="placeholder"
|
|
/>
|
|
<ul class="suggestion-box" v-if="showSuggestions">
|
|
<li v-for="(suggestion, i) in suggestions"
|
|
@click="selectSuggestion(suggestion)"
|
|
:class="{active: (i === active)}">{{suggestion}}</li>
|
|
</ul>
|
|
</div>
|
|
|
|
`;
|
|
|
|
function data() {
|
|
return {
|
|
suggestions: [],
|
|
showSuggestions: false,
|
|
active: 0,
|
|
};
|
|
}
|
|
|
|
const ajaxCache = {};
|
|
|
|
const props = ['url', 'type', 'value', 'placeholder', 'name'];
|
|
|
|
function getNameInputVal(valInput) {
|
|
let parentRow = valInput.parentNode.parentNode;
|
|
let nameInput = parentRow.querySelector('[autosuggest-type="name"]');
|
|
return (nameInput === null) ? '' : nameInput.value;
|
|
}
|
|
|
|
const methods = {
|
|
|
|
inputUpdate(inputValue) {
|
|
this.$emit('input', inputValue);
|
|
let params = {};
|
|
|
|
if (this.type === 'value') {
|
|
let nameVal = getNameInputVal(this.$el);
|
|
if (nameVal !== "") params.name = nameVal;
|
|
}
|
|
|
|
this.getSuggestions(inputValue.slice(0, 3), params).then(suggestions => {
|
|
if (inputValue.length === 0) {
|
|
this.displaySuggestions(suggestions.slice(0, 6));
|
|
return;
|
|
}
|
|
// Filter to suggestions containing searched term
|
|
suggestions = suggestions.filter(item => {
|
|
return item.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1;
|
|
}).slice(0, 4);
|
|
this.displaySuggestions(suggestions);
|
|
});
|
|
},
|
|
|
|
inputBlur() {
|
|
setTimeout(() => {
|
|
this.$emit('blur');
|
|
this.showSuggestions = false;
|
|
}, 100);
|
|
},
|
|
|
|
inputKeydown(event) {
|
|
if (event.key === 'Enter') event.preventDefault();
|
|
if (!this.showSuggestions) return;
|
|
|
|
// Down arrow
|
|
if (event.key === 'ArrowDown') {
|
|
this.active = (this.active === this.suggestions.length - 1) ? 0 : this.active+1;
|
|
}
|
|
// Up Arrow
|
|
else if (event.key === 'ArrowUp') {
|
|
this.active = (this.active === 0) ? this.suggestions.length - 1 : this.active-1;
|
|
}
|
|
// Enter key
|
|
else if ((event.key === 'Enter') && !event.shiftKey) {
|
|
this.selectSuggestion(this.suggestions[this.active]);
|
|
}
|
|
// Escape key
|
|
else if (event.key === 'Escape') {
|
|
this.showSuggestions = false;
|
|
}
|
|
},
|
|
|
|
displaySuggestions(suggestions) {
|
|
if (suggestions.length === 0) {
|
|
this.suggestions = [];
|
|
this.showSuggestions = false;
|
|
return;
|
|
}
|
|
|
|
this.suggestions = suggestions;
|
|
this.showSuggestions = true;
|
|
this.active = 0;
|
|
},
|
|
|
|
selectSuggestion(suggestion) {
|
|
this.$refs.input.value = suggestion;
|
|
this.$refs.input.focus();
|
|
this.$emit('input', suggestion);
|
|
this.showSuggestions = false;
|
|
},
|
|
|
|
/**
|
|
* Get suggestions from BookStack. Store and use local cache if already searched.
|
|
* @param {String} input
|
|
* @param {Object} params
|
|
*/
|
|
getSuggestions(input, params) {
|
|
params.search = input;
|
|
const cacheKey = `${this.url}:${JSON.stringify(params)}`;
|
|
|
|
if (typeof ajaxCache[cacheKey] !== "undefined") {
|
|
return Promise.resolve(ajaxCache[cacheKey]);
|
|
}
|
|
|
|
return this.$http.get(this.url, params).then(resp => {
|
|
ajaxCache[cacheKey] = resp.data;
|
|
return resp.data;
|
|
});
|
|
}
|
|
|
|
};
|
|
|
|
export default {template, data, props, methods}; |