1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

vdoc: implement keyboard shortcuts for search navigation (#19088)

This commit is contained in:
Turiiya 2023-08-09 12:53:15 +02:00 committed by GitHub
parent b7afe6b236
commit 64029a2980
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 98 additions and 18 deletions

View File

@ -197,9 +197,14 @@ body {
width: 1.2rem; width: 1.2rem;
height: 1.2rem; height: 1.2rem;
} }
.doc-nav > .heading-container > .heading > #search { .doc-nav #search {
position: relative;
margin: 0.6rem 1.2rem 1rem 1.2rem; margin: 0.6rem 1.2rem 1rem 1.2rem;
display: flex;
}
.doc-nav #search input {
border: none; border: none;
width: 100%;
border-radius: 0.2rem; border-radius: 0.2rem;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
outline: none; outline: none;
@ -208,17 +213,32 @@ body {
color: #fff; color: #fff;
color: var(--menu-search-text-color); color: var(--menu-search-text-color);
} }
.doc-nav > .heading-container > .heading > #search::placeholder { .doc-nav #search input::placeholder {
color: #edf2f7; color: #edf2f7;
text-transform: uppercase; text-transform: uppercase;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
} }
.doc-nav > .heading-container > .heading > #search:-ms-input-placeholder { .doc-nav #search-keys {
color: #edf2f7; position: absolute;
text-transform: uppercase; height: 100%;
font-size: 12px; align-items: center;
font-weight: 600; display: flex;
top: 0;
right: 0.75rem;
opacity: 0.33;
transition: opacity 0.1s;
}
.doc-nav #search-keys.hide {
opacity: 0;
}
.doc-nav #search-keys kbd {
padding: 2.5px 3px;
margin-left: 1px;
font-size: 11px;
background-color: var(--menu-background-color);
border: 1px solid #ffffff44;
border-radius: 3px;
} }
.doc-nav > .content { .doc-nav > .content {
padding: 0 2rem 2rem 2rem; padding: 0 2rem 2rem 2rem;
@ -278,7 +298,8 @@ body {
.doc-nav .search li { .doc-nav .search li {
line-height: 1.5; line-height: 1.5;
} }
.doc-nav > .search .result:hover { .doc-nav > .search .result:hover,
.doc-nav > .search .result.selected {
background-color: #00000021; background-color: #00000021;
background-color: var(--menu-search-result-background-hover-color); background-color: var(--menu-search-result-background-hover-color);
} }

View File

@ -49,7 +49,6 @@ function setupMobileToggle() {
const isHidden = docNav.classList.contains('hidden'); const isHidden = docNav.classList.contains('hidden');
docNav.classList.toggle('hidden'); docNav.classList.toggle('hidden');
const search = docNav.querySelector('.search'); const search = docNav.querySelector('.search');
// console.log(search);
const searchHasResults = search.classList.contains('has-results'); const searchHasResults = search.classList.contains('has-results');
if (isHidden && searchHasResults) { if (isHidden && searchHasResults) {
search.classList.remove('mobile-hidden'); search.classList.remove('mobile-hidden');
@ -145,6 +144,72 @@ function setupSearch() {
} }
}); });
searchInput.addEventListener('input', onInputChange); searchInput.addEventListener('input', onInputChange);
setupSearchKeymaps();
}
function setupSearchKeymaps() {
const searchInput = document.querySelector('#search input');
// Keyboard shortcut indicator
const searchKeys = document.createElement('div');
const modifierKeyPrefix = navigator.platform.includes('Mac') ? '⌘' : 'Ctrl';
searchKeys.setAttribute('id', 'search-keys');
searchKeys.innerHTML = '<kbd>' + modifierKeyPrefix + '</kbd><kbd>k</kbd>';
searchInput.parentElement?.appendChild(searchKeys);
searchInput.addEventListener('focus', () => searchKeys.classList.add('hide'));
searchInput.addEventListener('blur', () => searchKeys.classList.remove('hide'));
// Global shortcuts to focus searchInput
document.addEventListener('keydown', (ev) => {
if (ev.key === '/' || ((ev.ctrlKey || ev.metaKey) && ev.key === 'k')) {
ev.preventDefault();
searchInput.focus();
}
});
// Shortcuts while searchInput is focused
let selectedIdx = -1;
function selectResult(results, newIdx) {
if (selectedIdx !== -1) {
results[selectedIdx].classList.remove('selected');
}
results[newIdx].classList.add('selected');
results[newIdx].scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' });
selectedIdx = newIdx;
}
searchInput.addEventListener('keydown', (ev) => {
const searchResults = document.querySelectorAll('.search .result');
switch (ev.key) {
case 'Escape':
searchInput.blur();
break;
case 'Enter':
if (!searchResults.length || selectedIdx === -1) break;
searchResults[selectedIdx].querySelector('a').click();
break;
case 'ArrowDown':
ev.preventDefault();
if (!searchResults.length) break;
if (selectedIdx >= searchResults.length - 1) {
// Cycle to first if last is selected
selectResult(searchResults, 0);
} else {
// Select next
selectResult(searchResults, selectedIdx + 1);
}
break;
case 'ArrowUp':
ev.preventDefault();
if (!searchResults.length) break;
if (selectedIdx <= 0) {
// Cycle to last if first is selected (or select it if none is selcted yet)
selectResult(searchResults, searchResults.length - 1);
} else {
// Select previous
selectResult(searchResults, selectedIdx - 1);
}
break;
default:
selectedIdx = -1;
}
});
} }
function createSearchResult(data) { function createSearchResult(data) {
@ -194,11 +259,3 @@ function debounce(func, timeout) {
timer = setTimeout(next, timeout > 0 ? timeout : 300); timer = setTimeout(next, timeout > 0 ? timeout : 300);
}; };
} }
document.addEventListener('keypress', (ev) => {
if (ev.key == '/') {
const search = document.getElementById('search');
ev.preventDefault();
search.focus();
}
});

View File

@ -46,7 +46,9 @@
</div> </div>
{{ menu_icon }} {{ menu_icon }}
</div> </div>
<input type="text" id="search" placeholder="Search... (beta)" autocomplete="off" /> <div id="search">
<input type="text" placeholder="Search... (beta)" autocomplete="off" />
</div>
</div> </div>
</div> </div>
<nav class="search hidden"></nav> <nav class="search hidden"></nav>