add incremental search

* support modals in scrollElementIntoView and incremental search
This commit is contained in:
tophf 2022-01-25 22:52:37 +03:00
parent 79fcb5705a
commit 30eb6ed4f1
6 changed files with 46 additions and 22 deletions

View File

@ -2,6 +2,10 @@
height: 100%;
max-width: 80vw;
}
.injection-order #incremental-search {
transform: scaleY(.55);
transform-origin: top;
}
.injection-order #message-box-contents,
.injection-order section {
padding: 0;
@ -19,7 +23,7 @@
box-sizing: border-box;
}
.injection-order ol {
padding: 1px 0; /* 1px for keyboard-focused element's outline */
padding: 0;
margin: 0;
font-size: 14px;
overflow-y: auto;
@ -38,6 +42,8 @@
.injection-order-entry {
display: flex;
justify-content: space-between;
position: relative; /* for incremental-search */
padding: 1px 1px 1px 1rem; /* keyboard focus outline */
color: #000;
transition: transform .25s ease-in-out;
z-index: 1;
@ -48,7 +54,7 @@
cursor: move;
}
.injection-order-entry a[href] {
padding: .4em 0 .4em 1rem;
padding: .4em 0;
cursor: inherit;
}
.injection-order-entry.enabled a[href] {

View File

@ -36,7 +36,9 @@ async function InjectionOrder(show = true) {
entry.classList.toggle('enabled', style.enabled);
parts.name.href = '/edit.html?id=' + style.id;
parts.name.textContent = style.name;
return entry.cloneNode(true);
return Object.assign(entry.cloneNode(true), {
styleNameLowerCase: style.name.toLocaleLowerCase(),
});
}
function makeList([type, styles]) {

View File

@ -280,13 +280,14 @@ function onDOMready() {
function scrollElementIntoView(element, {invalidMarginRatio = 0} = {}) {
// align to the top/bottom of the visible area if wasn't visible
if (!element.parentNode) return;
const parent = element.parentNode;
if (!parent) return;
const {top, height} = element.getBoundingClientRect();
const {top: parentTop, bottom: parentBottom} = element.parentNode.getBoundingClientRect();
const {top: parentTop, bottom: parentBottom} = parent.getBoundingClientRect();
const windowHeight = window.innerHeight;
if (top < Math.max(parentTop, windowHeight * invalidMarginRatio) ||
top > Math.min(parentBottom, windowHeight) - height - windowHeight * invalidMarginRatio) {
window.scrollBy(0, top - windowHeight / 2 + height);
parent.scrollBy(0, top - windowHeight / 2 + height);
}
}

View File

@ -0,0 +1,12 @@
#incremental-search {
position: absolute;
color: transparent;
border: 1px solid hsla(180, 100%, 50%, .25);
margin: -1px -2px;
overflow: hidden;
resize: none;
background-color: hsla(180, 100%, 50%, .1);
box-sizing: content-box;
pointer-events: none;
z-index: 2147483647,
}

View File

@ -1,6 +1,7 @@
/* global debounce */// toolbox.js
/* global installed */// manage.js
/* global
$$
$
$create
$isTextInput
@ -14,39 +15,35 @@
let prevTime = performance.now();
let focusedName = '';
const input = $create('textarea', {
id: 'incremental-search',
spellcheck: false,
attributes: {tabindex: -1},
oninput: incrementalSearch,
});
replaceInlineStyle({
opacity: '0',
position: 'absolute',
color: 'transparent',
border: '1px solid hsla(180, 100%, 100%, .5)',
margin: '-1px -2px',
overflow: 'hidden',
resize: 'none',
'background-color': 'hsla(180, 100%, 100%, .2)',
'box-sizing': 'content-box',
'pointer-events': 'none',
});
document.body.appendChild(input);
window.on('keydown', maybeRefocus, true);
function incrementalSearch({key}, immediately) {
function incrementalSearch(event, immediately) {
const {key} = event;
if (!immediately) {
debounce(incrementalSearch, 100, {}, true);
return;
}
const direction = key === 'ArrowUp' ? -1 : key === 'ArrowDown' ? 1 : 0;
const text = input.value.toLocaleLowerCase();
if (direction) {
event.preventDefault();
}
if (!text.trim() || !direction && (text === prevText || focusedName.startsWith(text))) {
prevText = text;
return;
}
let textAtPos = 1e6;
let rotated;
const entries = [...installed.children];
const entries = $('#message-box') ? $$('.injection-order-entry') : [...installed.children];
const focusedIndex = entries.indexOf(focusedEntry);
if (focusedIndex > 0) {
if (direction > 0) {
@ -74,7 +71,7 @@
}
if (found && found !== focusedEntry) {
focusedEntry = found;
focusedLink = $('.style-name-link', found);
focusedLink = $('a', found);
focusedName = found.styleNameLowerCase;
scrollElementIntoView(found, {invalidMarginRatio: .25});
animateElement(found, 'highlight-quick');
@ -84,12 +81,17 @@
opacity: '1',
});
focusedLink.prepend(input);
input.focus();
return true;
}
}
function maybeRefocus(event) {
if (event.altKey || event.metaKey || $('#message-box')) {
if (event.altKey || event.metaKey) {
return;
}
const modal = $('#message-box');
if (modal && !modal.classList.contains('injection-order')) {
return;
}
const inTextInput = $isTextInput(event.target);
@ -99,7 +101,7 @@
(code === 'Slash' || key === '/') && !ctrl && !inTextInput) {
// focus search field on "/" or Ctrl-F key
event.preventDefault();
$('#search').focus();
if (!modal) $('#search').focus();
return;
}
if (ctrl || inTextInput && event.target !== input) {

View File

@ -92,11 +92,12 @@ newUI.renderClass();
showStyles(styles, ids);
require([
window.on('load', () => require([
'/manage/import-export',
'/manage/incremental-search.css',
'/manage/incremental-search',
'/manage/updater-ui',
]);
]), {once: true});
})();
msg.onExtension(onRuntimeMessage);