manage: search regexps, add search help, fix favicons toggle icon

* the toggle icon is now a triangle, rotated when open
This commit is contained in:
tophf 2017-12-06 09:39:45 +03:00
parent 69ee57f88f
commit 03b6f6c263
6 changed files with 120 additions and 21 deletions

View File

@ -691,9 +691,9 @@
"message": "Search contents", "message": "Search contents",
"description": "Label for the search filter textbox on the Manage styles page" "description": "Label for the search filter textbox on the Manage styles page"
}, },
"searchStylesTooltip": { "searchStylesHelp": {
"message": "To show styles for a URL, prefix it with 'url:'\nFor example, url:https://github.com/openstyles/stylus", "message": "</> key focuses the search field.\nPlain text: search within the name, code, homepage URL and sites it is applied to. Words with less than 3 letters are ignored.\nMatching URL: prefix the search with <url:>, e.g. <url:https://github.com/openstyles/stylus>\nRegular expressions: include slashes and flags, e.g. </foo.*?/simguy>\nExact words: wrap the query in double quotes, e.g. <\"Like this!\">",
"description": "Label for the search filter textbox on the Manage styles page" "description": "Text in the minihelp displayed when clicking (i) icon to the right of the search input field on the Manage styles page"
}, },
"sectionAdd": { "sectionAdd": {
"message": "Add another section", "message": "Add another section",

View File

@ -286,9 +286,9 @@ function tryCatch(func, ...args) {
} }
function tryRegExp(regexp) { function tryRegExp(regexp, flags) {
try { try {
return new RegExp(regexp); return new RegExp(regexp, flags);
} catch (e) {} } catch (e) {}
} }

View File

@ -237,10 +237,12 @@
<span i18n-text="manageOnlyUpdates"></span> <span i18n-text="manageOnlyUpdates"></span>
</label> </label>
<div id="search-wrapper">
<input id="search" type="search" i18n-placeholder="searchStyles" spellcheck="false" <input id="search" type="search" i18n-placeholder="searchStyles" spellcheck="false"
i18n-title="searchStylesTooltip"
data-filter=":not(.not-matching)" data-filter=":not(.not-matching)"
data-filter-hide=".not-matching"> data-filter-hide=".not-matching">
<svg id="search-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg>
</div>
</details> </details>
@ -292,10 +294,11 @@
<label for="manage.newUI.favicons" i18n-text="manageFavicons"> <label for="manage.newUI.favicons" i18n-text="manageFavicons">
<input id="manage.newUI.favicons" type="checkbox"> <input id="manage.newUI.favicons" type="checkbox">
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg> <svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
</label> <svg class="svg-icon select-arrow" data-toggle-on-click="#faviconsHelp">
<svg class="svg-icon info" viewBox="0 0 14 16" i18n-alt="helpAlt" data-toggle-on-click="#faviconsHelp"> <title i18n-text="optionsSubheading"></title>
<path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path> <use xlink:href="#svg-icon-select-arrow"/>
</svg> </svg>
</label>
<div id="faviconsHelp" class="hidden" i18n-text="manageFaviconsHelp"> <div id="faviconsHelp" class="hidden" i18n-text="manageFaviconsHelp">
<div> <div>
<label for="manage.newUI.faviconsGray" i18n-text="manageFaviconsGray"> <label for="manage.newUI.faviconsGray" i18n-text="manageFaviconsGray">
@ -348,6 +351,11 @@
<symbol id="svg-icon-select-arrow" viewBox="0 0 1792 1792"> <symbol id="svg-icon-select-arrow" viewBox="0 0 1792 1792">
<path fill-rule="evenodd" d="M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"/> <path fill-rule="evenodd" d="M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z"/>
</symbol> </symbol>
<symbol id="svg-icon-help" viewBox="0 0 14 16">
<title i18n-text="helpAlt"></title>
<path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path>
</symbol>
</svg> </svg>
</body> </body>

View File

@ -1,4 +1,4 @@
/* global installed */ /* global installed messageBox */
'use strict'; 'use strict';
const filtersSelector = { const filtersSelector = {
@ -31,6 +31,25 @@ onDOMready().then(onBackgroundReady).then(() => {
if (urlFilterParam) { if (urlFilterParam) {
$('#search').value = 'url:' + urlFilterParam; $('#search').value = 'url:' + urlFilterParam;
} }
$('#search-help').onclick = () => {
messageBox({
className: 'help-text',
title: t('searchStyles'),
contents:
$create('ul',
t('searchStylesHelp').split('\n').map(line =>
$create('li', line.split(/(<.*?>)/).map((s, i, words) => {
if (s.startsWith('<')) {
const num = words.length;
const className = i === num - 2 && !words[num - 1] ? '.last' : '';
return $create('mark' + className, s.slice(1, -1));
} else {
return s;
}
})))),
buttons: [t('confirmOK')],
});
};
$$('select[id$=".invert"]').forEach(el => { $$('select[id$=".invert"]').forEach(el => {
const slave = $('#' + el.id.replace('.invert', '')); const slave = $('#' + el.id.replace('.invert', ''));
@ -350,10 +369,11 @@ function showFiltersStats() {
function searchStyles({immediately, container}) { function searchStyles({immediately, container}) {
const searchElement = $('#search'); const searchElement = $('#search');
const urlMode = /^\s*url:/i.test(searchElement.value); const value = searchElement.value;
const urlMode = /^\s*url:/i.test(value);
const query = urlMode const query = urlMode
? searchElement.value.replace(/^\s*url:/i, '').trim() ? value.replace(/^\s*url:/i, '').trim()
: searchElement.value.toLocaleLowerCase(); : value.toLocaleLowerCase();
const queryPrev = searchElement.lastValue || ''; const queryPrev = searchElement.lastValue || '';
if (query === queryPrev && !immediately && !container) { if (query === queryPrev && !immediately && !container) {
return; return;
@ -364,6 +384,12 @@ function searchStyles({immediately, container}) {
} }
searchElement.lastValue = query; searchElement.lastValue = query;
const trimmed = query.trim();
const rx = trimmed.startsWith('/') && trimmed.indexOf('/', 1) > 0 &&
tryRegExp(...(value.match(/^\s*\/(.*?)\/([gimsuy]*)\s*$/) || []).slice(1));
const words = rx ? null :
trimmed.startsWith('"') && trimmed.endsWith('"') ? [value.trim().slice(1, -1)] :
query.split(/\s+/).filter(s => s.length > 2);
const searchInVisible = !urlMode && queryPrev && query.includes(queryPrev); const searchInVisible = !urlMode && queryPrev && query.includes(queryPrev);
const entries = container && container.children || container || const entries = container && container.children || container ||
(searchInVisible ? $$('.entry:not(.hidden)') : installed.children); (searchInVisible ? $$('.entry:not(.hidden)') : installed.children);
@ -415,6 +441,17 @@ function searchStyles({immediately, container}) {
} }
function isMatchingText(text) { function isMatchingText(text) {
return text.toLocaleLowerCase().indexOf(query) >= 0; if (rx) {
return rx.test(text);
}
for (let pass = 1; pass <= 2; pass++) {
for (const word of words) {
if (text.includes(word)) {
return true;
}
}
text = text.toLocaleLowerCase();
}
return false;
} }
} }

View File

@ -658,6 +658,7 @@ select {
align-items: center; align-items: center;
margin-bottom: auto; margin-bottom: auto;
flex-wrap: wrap; flex-wrap: wrap;
position: relative;
} }
#newUIoptions input[type="number"] { #newUIoptions input[type="number"] {
@ -665,6 +666,18 @@ select {
margin-right: .5em; margin-right: .5em;
} }
#newUIoptions [data-toggle-on-click] {
transform: rotate(-90deg);
cursor: pointer;
right: -16px;
top: 0;
pointer-events: auto;
}
#newUIoptions [data-toggle-on-click][open] {
transform: none;
}
input[id^="manage.newUI"] { input[id^="manage.newUI"] {
margin-left: 0; margin-left: 0;
} }
@ -763,7 +776,6 @@ input[id^="manage.newUI"] {
display: none !important; display: none !important;
} }
#filters input,
#filters label { #filters label {
display: flex; display: flex;
align-items: center; align-items: center;
@ -816,14 +828,48 @@ input[id^="manage.newUI"] {
display: none; display: none;
} }
#search-wrapper {
display: flex;
align-items: center;
flex-wrap: wrap;
}
#search { #search {
width: calc(100% - 4px); flex-grow: 1;
margin: 0.25rem 0 0; margin: 0.25rem 0 0;
border-radius: 0.25rem;
padding-left: 0.25rem; padding-left: 0.25rem;
border-width: 1px; border-width: 1px;
} }
#search-wrapper .info {
margin: 4px -5px 0 8px;
}
#message-box.help-text > div {
max-width: 26rem;
}
.help-text li:not(:last-child) {
margin-bottom: 1em;
}
.help-text mark {
background-color: rgba(128, 128, 128, .15);
color: currentColor;
padding: 2px 6px;
font-weight: bold;
font-family: monospace;
border: 1px solid rgba(128, 128, 128, .25);
display: inline-block;
margin: 2px;
}
.help-text mark.last {
display: block;
width: -moz-min-content;
width: min-content;
white-space: nowrap;
}
#import ul { #import ul {
margin-left: 0; margin-left: 0;
padding-left: 0; padding-left: 0;

View File

@ -66,7 +66,15 @@ function initGlobalEvents() {
$$('[data-toggle-on-click]').forEach(el => { $$('[data-toggle-on-click]').forEach(el => {
// dataset on SVG doesn't work in Chrome 49-??, works in 57+ // dataset on SVG doesn't work in Chrome 49-??, works in 57+
const target = $(el.getAttribute('data-toggle-on-click')); const target = $(el.getAttribute('data-toggle-on-click'));
el.onclick = () => target.classList.toggle('hidden'); el.onclick = event => {
event.preventDefault();
target.classList.toggle('hidden');
if (target.classList.contains('hidden')) {
el.removeAttribute('open');
} else {
el.setAttribute('open', '');
}
};
}); });
// triggered automatically by setupLivePrefs() below // triggered automatically by setupLivePrefs() below