add incremental search
* support modals in scrollElementIntoView and incremental search
This commit is contained in:
parent
79fcb5705a
commit
30eb6ed4f1
|
@ -2,6 +2,10 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-width: 80vw;
|
max-width: 80vw;
|
||||||
}
|
}
|
||||||
|
.injection-order #incremental-search {
|
||||||
|
transform: scaleY(.55);
|
||||||
|
transform-origin: top;
|
||||||
|
}
|
||||||
.injection-order #message-box-contents,
|
.injection-order #message-box-contents,
|
||||||
.injection-order section {
|
.injection-order section {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -19,7 +23,7 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.injection-order ol {
|
.injection-order ol {
|
||||||
padding: 1px 0; /* 1px for keyboard-focused element's outline */
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
@ -38,6 +42,8 @@
|
||||||
.injection-order-entry {
|
.injection-order-entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
position: relative; /* for incremental-search */
|
||||||
|
padding: 1px 1px 1px 1rem; /* keyboard focus outline */
|
||||||
color: #000;
|
color: #000;
|
||||||
transition: transform .25s ease-in-out;
|
transition: transform .25s ease-in-out;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
@ -48,7 +54,7 @@
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
.injection-order-entry a[href] {
|
.injection-order-entry a[href] {
|
||||||
padding: .4em 0 .4em 1rem;
|
padding: .4em 0;
|
||||||
cursor: inherit;
|
cursor: inherit;
|
||||||
}
|
}
|
||||||
.injection-order-entry.enabled a[href] {
|
.injection-order-entry.enabled a[href] {
|
||||||
|
|
|
@ -36,7 +36,9 @@ async function InjectionOrder(show = true) {
|
||||||
entry.classList.toggle('enabled', style.enabled);
|
entry.classList.toggle('enabled', style.enabled);
|
||||||
parts.name.href = '/edit.html?id=' + style.id;
|
parts.name.href = '/edit.html?id=' + style.id;
|
||||||
parts.name.textContent = style.name;
|
parts.name.textContent = style.name;
|
||||||
return entry.cloneNode(true);
|
return Object.assign(entry.cloneNode(true), {
|
||||||
|
styleNameLowerCase: style.name.toLocaleLowerCase(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeList([type, styles]) {
|
function makeList([type, styles]) {
|
||||||
|
|
|
@ -280,13 +280,14 @@ function onDOMready() {
|
||||||
|
|
||||||
function scrollElementIntoView(element, {invalidMarginRatio = 0} = {}) {
|
function scrollElementIntoView(element, {invalidMarginRatio = 0} = {}) {
|
||||||
// align to the top/bottom of the visible area if wasn't visible
|
// 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, height} = element.getBoundingClientRect();
|
||||||
const {top: parentTop, bottom: parentBottom} = element.parentNode.getBoundingClientRect();
|
const {top: parentTop, bottom: parentBottom} = parent.getBoundingClientRect();
|
||||||
const windowHeight = window.innerHeight;
|
const windowHeight = window.innerHeight;
|
||||||
if (top < Math.max(parentTop, windowHeight * invalidMarginRatio) ||
|
if (top < Math.max(parentTop, windowHeight * invalidMarginRatio) ||
|
||||||
top > Math.min(parentBottom, windowHeight) - height - windowHeight * invalidMarginRatio) {
|
top > Math.min(parentBottom, windowHeight) - height - windowHeight * invalidMarginRatio) {
|
||||||
window.scrollBy(0, top - windowHeight / 2 + height);
|
parent.scrollBy(0, top - windowHeight / 2 + height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
manage/incremental-search.css
Normal file
12
manage/incremental-search.css
Normal 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,
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
/* global debounce */// toolbox.js
|
/* global debounce */// toolbox.js
|
||||||
/* global installed */// manage.js
|
/* global installed */// manage.js
|
||||||
/* global
|
/* global
|
||||||
|
$$
|
||||||
$
|
$
|
||||||
$create
|
$create
|
||||||
$isTextInput
|
$isTextInput
|
||||||
|
@ -14,39 +15,35 @@
|
||||||
let prevTime = performance.now();
|
let prevTime = performance.now();
|
||||||
let focusedName = '';
|
let focusedName = '';
|
||||||
const input = $create('textarea', {
|
const input = $create('textarea', {
|
||||||
|
id: 'incremental-search',
|
||||||
spellcheck: false,
|
spellcheck: false,
|
||||||
attributes: {tabindex: -1},
|
attributes: {tabindex: -1},
|
||||||
oninput: incrementalSearch,
|
oninput: incrementalSearch,
|
||||||
});
|
});
|
||||||
replaceInlineStyle({
|
replaceInlineStyle({
|
||||||
opacity: '0',
|
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);
|
document.body.appendChild(input);
|
||||||
window.on('keydown', maybeRefocus, true);
|
window.on('keydown', maybeRefocus, true);
|
||||||
|
|
||||||
function incrementalSearch({key}, immediately) {
|
function incrementalSearch(event, immediately) {
|
||||||
|
const {key} = event;
|
||||||
if (!immediately) {
|
if (!immediately) {
|
||||||
debounce(incrementalSearch, 100, {}, true);
|
debounce(incrementalSearch, 100, {}, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const direction = key === 'ArrowUp' ? -1 : key === 'ArrowDown' ? 1 : 0;
|
const direction = key === 'ArrowUp' ? -1 : key === 'ArrowDown' ? 1 : 0;
|
||||||
const text = input.value.toLocaleLowerCase();
|
const text = input.value.toLocaleLowerCase();
|
||||||
|
if (direction) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
if (!text.trim() || !direction && (text === prevText || focusedName.startsWith(text))) {
|
if (!text.trim() || !direction && (text === prevText || focusedName.startsWith(text))) {
|
||||||
prevText = text;
|
prevText = text;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let textAtPos = 1e6;
|
let textAtPos = 1e6;
|
||||||
let rotated;
|
let rotated;
|
||||||
const entries = [...installed.children];
|
const entries = $('#message-box') ? $$('.injection-order-entry') : [...installed.children];
|
||||||
const focusedIndex = entries.indexOf(focusedEntry);
|
const focusedIndex = entries.indexOf(focusedEntry);
|
||||||
if (focusedIndex > 0) {
|
if (focusedIndex > 0) {
|
||||||
if (direction > 0) {
|
if (direction > 0) {
|
||||||
|
@ -74,7 +71,7 @@
|
||||||
}
|
}
|
||||||
if (found && found !== focusedEntry) {
|
if (found && found !== focusedEntry) {
|
||||||
focusedEntry = found;
|
focusedEntry = found;
|
||||||
focusedLink = $('.style-name-link', found);
|
focusedLink = $('a', found);
|
||||||
focusedName = found.styleNameLowerCase;
|
focusedName = found.styleNameLowerCase;
|
||||||
scrollElementIntoView(found, {invalidMarginRatio: .25});
|
scrollElementIntoView(found, {invalidMarginRatio: .25});
|
||||||
animateElement(found, 'highlight-quick');
|
animateElement(found, 'highlight-quick');
|
||||||
|
@ -84,12 +81,17 @@
|
||||||
opacity: '1',
|
opacity: '1',
|
||||||
});
|
});
|
||||||
focusedLink.prepend(input);
|
focusedLink.prepend(input);
|
||||||
|
input.focus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeRefocus(event) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const inTextInput = $isTextInput(event.target);
|
const inTextInput = $isTextInput(event.target);
|
||||||
|
@ -99,7 +101,7 @@
|
||||||
(code === 'Slash' || key === '/') && !ctrl && !inTextInput) {
|
(code === 'Slash' || key === '/') && !ctrl && !inTextInput) {
|
||||||
// focus search field on "/" or Ctrl-F key
|
// focus search field on "/" or Ctrl-F key
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
$('#search').focus();
|
if (!modal) $('#search').focus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ctrl || inTextInput && event.target !== input) {
|
if (ctrl || inTextInput && event.target !== input) {
|
||||||
|
|
|
@ -92,11 +92,12 @@ newUI.renderClass();
|
||||||
|
|
||||||
showStyles(styles, ids);
|
showStyles(styles, ids);
|
||||||
|
|
||||||
require([
|
window.on('load', () => require([
|
||||||
'/manage/import-export',
|
'/manage/import-export',
|
||||||
|
'/manage/incremental-search.css',
|
||||||
'/manage/incremental-search',
|
'/manage/incremental-search',
|
||||||
'/manage/updater-ui',
|
'/manage/updater-ui',
|
||||||
]);
|
]), {once: true});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
msg.onExtension(onRuntimeMessage);
|
msg.onExtension(onRuntimeMessage);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user