mirror of
https://github.com/xvvvyz/tilde.git
synced 2026-03-11 14:44:24 +00:00
add suggestion preview functionality
This commit is contained in:
parent
bfdb4d5063
commit
ca2d6d65ec
1 changed files with 80 additions and 50 deletions
130
index.html
130
index.html
|
|
@ -172,8 +172,8 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-suggestion:hover,
|
.search-suggestion:focus,
|
||||||
.search-suggestion:focus {
|
.search-suggestion.highlight {
|
||||||
background: #333;
|
background: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -297,12 +297,27 @@
|
||||||
const $ = {
|
const $ = {
|
||||||
el: s => document.querySelector(s),
|
el: s => document.querySelector(s),
|
||||||
els: s => [].slice.call(document.querySelectorAll(s) || []),
|
els: s => [].slice.call(document.querySelectorAll(s) || []),
|
||||||
|
isDown: e => ['c-n', 'down'].includes($.key(e)),
|
||||||
|
isUp: e => ['c-p', 'up'].includes($.key(e)),
|
||||||
|
|
||||||
jsonp: url => {
|
jsonp: url => {
|
||||||
let script = document.createElement('script');
|
let script = document.createElement('script');
|
||||||
script.src = url;
|
script.src = url;
|
||||||
$.el('head').appendChild(script);
|
$.el('head').appendChild(script);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
key: e => {
|
||||||
|
const ctrl = e.ctrlKey;
|
||||||
|
|
||||||
|
switch (e.which) {
|
||||||
|
case 13: return 'enter';
|
||||||
|
case 27: return 'escape';
|
||||||
|
case 38: return 'up';
|
||||||
|
case 40: return 'down';
|
||||||
|
case 78: return ctrl ? 'c-n' : 'n';
|
||||||
|
case 80: return ctrl ? 'c-p' : 'n';
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
class Clock {
|
class Clock {
|
||||||
|
|
@ -368,9 +383,8 @@
|
||||||
)).join('');
|
)).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleKeydown(event) {
|
_handleKeydown(e) {
|
||||||
const isEsc = event.which === 27;
|
if ($.key(e) === 'escape') this.toggle(false);
|
||||||
if (isEsc) this.toggle(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerEvents() {
|
_registerEvents() {
|
||||||
|
|
@ -503,8 +517,7 @@
|
||||||
</li>`
|
</li>`
|
||||||
);
|
);
|
||||||
|
|
||||||
this._suggestionsEl.classList.add('active');
|
this._showSuggestions();
|
||||||
this._inputEl.classList.add('bottom-no-radius');
|
|
||||||
return ++this._totalSuggestions === CONFIG.suggestionsLimit;
|
return ++this._totalSuggestions === CONFIG.suggestionsLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -516,11 +529,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearSuggestions() {
|
_clearSuggestions() {
|
||||||
this._totalSuggestions = 0;
|
this._hideSuggestions();
|
||||||
this._clearClickEvents();
|
this._clearClickEvents();
|
||||||
this._suggestionsEl.innerHTML = '';
|
this._suggestionsEl.innerHTML = '';
|
||||||
this._suggestionsEl.classList.remove('active');
|
this._totalSuggestions = 0;
|
||||||
this._inputEl.classList.remove('bottom-no-radius');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// [[1, 2], [1, 2, 3, 4]] -> [1, 2, 3, 4]
|
// [[1, 2], [1, 2, 3, 4]] -> [1, 2, 3, 4]
|
||||||
|
|
@ -529,33 +541,29 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
_focusNext() {
|
_focusNext() {
|
||||||
if (this._suggestionEls.length) {
|
const active = $.el('.highlight');
|
||||||
const active = document.activeElement;
|
|
||||||
|
|
||||||
if (active.classList.contains('js-search-suggestion')) {
|
if (active) {
|
||||||
this._suggestionEls.forEach((el, index) => {
|
this._suggestionEls.forEach((el, index) => {
|
||||||
const nextSuggestion = this._suggestionEls[index + 1];
|
const next = this._suggestionEls[index + 1];
|
||||||
if (el === active && nextSuggestion) nextSuggestion.focus();
|
if (el === active && next) this._highlight(next);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this._suggestionEls[0].focus();
|
this._highlight(this._suggestionEls[0]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_focusPrevious() {
|
_focusPrevious() {
|
||||||
if (this._suggestionEls.length) {
|
const active = $.el('.highlight');
|
||||||
const active = document.activeElement;
|
|
||||||
|
|
||||||
if (active.classList.contains('js-search-suggestion')) {
|
if (active) {
|
||||||
this._suggestionEls.forEach((el, index) => {
|
this._suggestionEls.forEach((el, index) => {
|
||||||
if (el === active) {
|
if (el === active) {
|
||||||
const previousSuggestion = this._suggestionEls[index - 1];
|
const previous = this._suggestionEls[index - 1];
|
||||||
if (previousSuggestion) previousSuggestion.focus();
|
this._noHighlight();
|
||||||
else this._inputEl.focus();
|
if (previous) this._highlight(previous);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -564,24 +572,46 @@
|
||||||
.map(influencer => influencer.getSuggestionsPromise(input));
|
.map(influencer => influencer.getSuggestionsPromise(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleKeydown(event) {
|
_handleKeydown(e) {
|
||||||
const isDown = event.which === 40;
|
if ($.isDown(e) || $.isUp(e)) e.preventDefault();
|
||||||
const isUp = event.which === 38;
|
if ($.isDown(e)) this._focusNext();
|
||||||
const isCtrlN = event.which === 78 && event.ctrlKey;
|
if ($.isUp(e)) this._focusPrevious();
|
||||||
const isCtrlP = event.which === 80 && event.ctrlKey;
|
|
||||||
if (isDown || isUp || isCtrlN || isCtrlP) event.preventDefault();
|
|
||||||
if (isDown || isCtrlN) this._focusNext();
|
|
||||||
if (isUp || isCtrlP) this._focusPrevious();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_registerClickEvents() {
|
_highlight(el) {
|
||||||
|
this._noHighlight();
|
||||||
|
el.classList.add('highlight');
|
||||||
|
this._inputEl.value = el.getAttribute('data-suggestion');
|
||||||
|
this._inputEl.focus()
|
||||||
|
}
|
||||||
|
|
||||||
|
_noHighlight() {
|
||||||
|
this._inputEl.value = this._originalValue;
|
||||||
|
this._suggestionEls.forEach(el => el.classList.remove('highlight'));
|
||||||
|
}
|
||||||
|
|
||||||
|
_showSuggestions() {
|
||||||
|
this._inputEl.classList.add('bottom-no-radius');
|
||||||
|
this._suggestionsEl.classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
_hideSuggestions() {
|
||||||
|
this._inputEl.classList.remove('bottom-no-radius');
|
||||||
|
this._suggestionsEl.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
_registerEvents() {
|
||||||
this._suggestionEls.forEach(el => {
|
this._suggestionEls.forEach(el => {
|
||||||
const value = el.getAttribute('data-suggestion');
|
const value = el.getAttribute('data-suggestion');
|
||||||
|
el.addEventListener('mouseover', this._highlight.bind(this, el));
|
||||||
|
el.addEventListener('mouseout', this._noHighlight.bind(this));
|
||||||
el.addEventListener('click', this._handleClick.bind(null, value));
|
el.addEventListener('click', this._handleClick.bind(null, value));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_suggest(input) {
|
_suggest(input) {
|
||||||
|
this._originalValue = this._inputEl.value;
|
||||||
|
|
||||||
Promise.all(this._gatherInfluencers(input)).then(res => {
|
Promise.all(this._gatherInfluencers(input)).then(res => {
|
||||||
this._clearSuggestions();
|
this._clearSuggestions();
|
||||||
|
|
||||||
|
|
@ -589,7 +619,7 @@
|
||||||
.some(item => this._appendSuggestion(item, input));
|
.some(item => this._appendSuggestion(item, input));
|
||||||
|
|
||||||
this._suggestionEls = $.els('.js-search-suggestion');
|
this._suggestionEls = $.els('.js-search-suggestion');
|
||||||
this._registerClickEvents();
|
this._registerEvents();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -694,29 +724,29 @@
|
||||||
this._submitWithValue = this._submitWithValue.bind(this);
|
this._submitWithValue = this._submitWithValue.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleKeypress(event) {
|
_handleKeypress(e) {
|
||||||
const newChar = String.fromCharCode(event.which);
|
const newChar = String.fromCharCode(e.which);
|
||||||
const newQuery = this._inputEl.value + newChar;
|
const newQuery = this._inputEl.value + newChar;
|
||||||
const isNotEmpty = newChar.length;
|
const isNotEmpty = newChar.length;
|
||||||
const isEnterKey = event.which !== 13;
|
|
||||||
|
|
||||||
if (isNotEmpty && isEnterKey) {
|
if (isNotEmpty && $.key(e) !== 'enter') {
|
||||||
this._help.toggle(false);
|
this._help.toggle(false);
|
||||||
this._inputEl.focus();
|
this._inputEl.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CONFIG.instantRedirect) {
|
if (CONFIG.instantRedirect) {
|
||||||
this._queryParser
|
this._queryParser
|
||||||
.instantRedirect(event, newQuery, this._submitWithValue);
|
.instantRedirect(e, newQuery, this._submitWithValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleKeyup(event) {
|
_handleKeyup(e) {
|
||||||
const oldVal = this._inputElVal;
|
const oldVal = this._inputElVal;
|
||||||
const newVal = this._inputEl.value.trim();
|
const newVal = this._inputEl.value.trim();
|
||||||
|
const ignored = $.isDown(e) || $.isUp(e);
|
||||||
this._inputElVal = newVal;
|
this._inputElVal = newVal;
|
||||||
|
|
||||||
if (CONFIG.suggestions && oldVal !== newVal) {
|
if (CONFIG.suggestions && oldVal !== newVal && !ignored) {
|
||||||
this._suggester.suggest(newVal, this._submitWithValue);
|
this._suggester.suggest(newVal, this._submitWithValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -732,8 +762,8 @@
|
||||||
this._formEl.addEventListener('submit', this._submitForm, false);
|
this._formEl.addEventListener('submit', this._submitForm, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_submitForm(event) {
|
_submitForm(e) {
|
||||||
if (event) event.preventDefault();
|
if (e) e.preventDefault();
|
||||||
const query = this._inputEl.value.trim();
|
const query = this._inputEl.value.trim();
|
||||||
|
|
||||||
if (!query || query === '?') {
|
if (!query || query === '?') {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue