From 30eb6ed4f18b4bf5879a28518fc158ee5c1300ac Mon Sep 17 00:00:00 2001 From: tophf Date: Tue, 25 Jan 2022 22:52:37 +0300 Subject: [PATCH] add incremental search * support modals in scrollElementIntoView and incremental search --- injection-order/injection-order.css | 10 ++++++++-- injection-order/injection-order.js | 4 +++- js/dom.js | 7 ++++--- manage/incremental-search.css | 12 ++++++++++++ manage/incremental-search.js | 30 +++++++++++++++-------------- manage/manage.js | 5 +++-- 6 files changed, 46 insertions(+), 22 deletions(-) create mode 100644 manage/incremental-search.css diff --git a/injection-order/injection-order.css b/injection-order/injection-order.css index 561838ca..f864b3b6 100644 --- a/injection-order/injection-order.css +++ b/injection-order/injection-order.css @@ -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] { diff --git a/injection-order/injection-order.js b/injection-order/injection-order.js index fdc43e5e..5d447b7c 100644 --- a/injection-order/injection-order.js +++ b/injection-order/injection-order.js @@ -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]) { diff --git a/js/dom.js b/js/dom.js index 9d7671b5..8cfa73c6 100644 --- a/js/dom.js +++ b/js/dom.js @@ -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); } } diff --git a/manage/incremental-search.css b/manage/incremental-search.css new file mode 100644 index 00000000..e11c58e6 --- /dev/null +++ b/manage/incremental-search.css @@ -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, +} diff --git a/manage/incremental-search.js b/manage/incremental-search.js index 8b7ff460..168b6229 100644 --- a/manage/incremental-search.js +++ b/manage/incremental-search.js @@ -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) { diff --git a/manage/manage.js b/manage/manage.js index bb171d09..33db608c 100644 --- a/manage/manage.js +++ b/manage/manage.js @@ -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);