extract popup search + show error + CtrlF
This commit is contained in:
parent
3907116328
commit
a74ae5ac4f
|
@ -1,4 +1,4 @@
|
|||
/* global $$ waitForSelector */// dom.js
|
||||
/* global $$ $ waitForSelector */// dom.js
|
||||
/* global download */// toolbox.js
|
||||
'use strict';
|
||||
|
||||
|
@ -117,13 +117,22 @@ Object.assign(t, {
|
|||
return t.toFragment(root);
|
||||
},
|
||||
|
||||
fetchTemplate: async (url, name) => t.template[name] ||
|
||||
t.createTemplate({
|
||||
content: t.toFragment(t.parse(await download(url))),
|
||||
dataset: {id: name},
|
||||
}),
|
||||
fetchTemplate: async (url, name) => {
|
||||
let res = t.template[name];
|
||||
if (!res) {
|
||||
res = t.parse(await download(url), '*');
|
||||
if (!$$('template', res).map(t.createTemplate).length) {
|
||||
t.createTemplate({
|
||||
content: t.toFragment($('body', res)),
|
||||
dataset: {id: name},
|
||||
});
|
||||
}
|
||||
res = t.template[name];
|
||||
}
|
||||
return res;
|
||||
},
|
||||
|
||||
parse: str => new DOMParser().parseFromString(str, 'text/html').body,
|
||||
parse: (str, pick = 'body') => $(pick, new DOMParser().parseFromString(str, 'text/html')),
|
||||
|
||||
sanitizeHtml(root) {
|
||||
const toRemove = [];
|
||||
|
|
88
popup.html
88
popup.html
|
@ -98,65 +98,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<template data-id="searchResult">
|
||||
<div class="search-result">
|
||||
<a class="search-result-title"><span></span></a>
|
||||
<div class="search-result-info">
|
||||
<img class="search-result-screenshot" i18n="title:installButton">
|
||||
<div class="search-result-status"></div>
|
||||
<div class="search-result-actions">
|
||||
<button class="search-result-install" i18n="installButton"></button>
|
||||
<button class="search-result-uninstall" i18n="deleteStyleLabel"></button>
|
||||
<button class="search-result-customize" i18n="configureStyle"></button>
|
||||
</div>
|
||||
<dl class="search-result-meta">
|
||||
<div data-type="author">
|
||||
<dt i18n="author"></dt>
|
||||
<dd><a target="_blank" i18n="title:author"></a></dd>
|
||||
</div>
|
||||
<div data-type="rating">
|
||||
<dt i18n="searchResultRating"></dt>
|
||||
<dd i18n="title:searchResultRating"></dd>
|
||||
</div>
|
||||
<div data-type="updated">
|
||||
<dt i18n="searchResultUpdated"></dt>
|
||||
<dd i18n="title:searchResultUpdated"><time></time></dd>
|
||||
</div>
|
||||
<div data-type="weekly">
|
||||
<dt i18n="searchResultWeeklyCount"></dt>
|
||||
<dd i18n="title:searchResultWeeklyCount"></dd>
|
||||
</div>
|
||||
<div data-type="total">
|
||||
<dt i18n="searchResultInstallCount"></dt>
|
||||
<dd i18n="title:searchResultInstallCount"></dd>
|
||||
</div>
|
||||
</dl>
|
||||
<div class="search-result-description"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template data-id="searchNav">
|
||||
<div>
|
||||
<button data-type="prev" i18n="title:paginationPrevious" disabled>◄</button>
|
||||
<label>
|
||||
<span data-type="page" i18n="title:paginationCurrent">-</span>
|
||||
/
|
||||
<span data-type="total" i18n="title:paginationEstimated">-</span>
|
||||
</label>
|
||||
<button data-type="next" i18n="title:paginationNext" disabled>►</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template data-id="emptySearchResult">
|
||||
<div class="search-result-empty"></div>
|
||||
</template>
|
||||
|
||||
<template data-id="searchResultNotMatching">
|
||||
<p class="not-matching-explainer"
|
||||
i18n="searchResultNotMatching, title:searchResultNotMatchingNote"></p>
|
||||
</template>
|
||||
|
||||
<script src="js/polyfill.js"></script>
|
||||
<script src="js/msg.js"></script>
|
||||
<script src="js/toolbox.js"></script>
|
||||
|
@ -214,7 +155,7 @@
|
|||
><button class="if-not-blocked split-btn-pedal" i18n="menu-site:popupManageSiteStyles"></button>
|
||||
</div>
|
||||
<div class="split-btn if-not-blocked" id="find-split">
|
||||
<button id="find-styles-btn" i18n="findStyles"></button
|
||||
<button id="find-styles-btn" i18n="findStyles" title="Ctrl-F"></button
|
||||
><button class="split-btn-pedal"
|
||||
menu-usoa="UserStyles Archive"
|
||||
menu-usw="UserStyles World"
|
||||
|
@ -231,33 +172,6 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div id="search-results-error" class="hidden"></div>
|
||||
<div id="search-results" class="hidden">
|
||||
<div class="search-results-nav" data-type="top"></div>
|
||||
<div id="search-params">
|
||||
<input id="search-query" type="search" i18n="placeholder:search, title:searchStyleQueryHint">
|
||||
<div class="select-resizer">
|
||||
<select id="search-order" i18n="title:sortStylesHelpTitle">
|
||||
<option value="n" i18n="genericTitle">
|
||||
<option value="u" i18n="searchResultUpdated">
|
||||
<option value="t" i18n="searchResultInstallCount">
|
||||
<option value="w" i18n="searchResultWeeklyCount">
|
||||
<option value="r" i18n="searchResultRating">
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</div>
|
||||
<label>
|
||||
<span class="checkbox-container">
|
||||
<input id="search-globals" type="checkbox" checked>
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
</span>
|
||||
<span i18n="searchGlobalStyles"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="search-results-list"></div>
|
||||
<div class="search-results-nav" data-type="bottom"></div>
|
||||
</div>
|
||||
|
||||
<!-- Here we can use the above elements before DOMContentLoaded -->
|
||||
<script src="popup/events.js"></script>
|
||||
<script src="popup/popup.js"></script>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global $ $$ $create setupLivePrefs */// dom.js
|
||||
/* global $ $$ $create getEventKeyName setupLivePrefs */// dom.js
|
||||
/* global ABOUT_BLANK getStyleDataMerged preinit */// preinit.js
|
||||
/* global API msg */// msg.js
|
||||
/* global Events */
|
||||
|
@ -86,13 +86,30 @@ async function initPopup(frames) {
|
|||
setupLivePrefs();
|
||||
|
||||
const elFind = $('#find-styles-btn');
|
||||
elFind.onclick = async e => {
|
||||
const inline = e.type === 'click';
|
||||
if (inline) elFind.disabled = true;
|
||||
await require(['/popup/search']);
|
||||
if (!inline) Events.searchSite(e);
|
||||
const elFindDeps = async () => {
|
||||
if (!t.template.searchUI) {
|
||||
document.body.append(await t.fetchTemplate('/popup/search.html', 'searchUI'));
|
||||
}
|
||||
await require([
|
||||
'/popup/search.css',
|
||||
'/popup/search',
|
||||
]);
|
||||
};
|
||||
elFind.on('split-btn', elFind.onclick);
|
||||
elFind.on('click', async () => {
|
||||
elFind.disabled = true;
|
||||
await elFindDeps();
|
||||
Events.searchInline();
|
||||
});
|
||||
elFind.on('split-btn', async e => {
|
||||
await elFindDeps();
|
||||
Events.searchSite(e);
|
||||
});
|
||||
window.on('keydown', e => {
|
||||
if (getEventKeyName(e) === 'Ctrl-F') {
|
||||
e.preventDefault();
|
||||
elFind.click();
|
||||
}
|
||||
});
|
||||
|
||||
Object.assign($('#popup-manage-button'), {
|
||||
onclick: Events.openManager,
|
||||
|
|
84
popup/search.html
Normal file
84
popup/search.html
Normal file
|
@ -0,0 +1,84 @@
|
|||
<template data-id="searchUI">
|
||||
<div id="search-results-error" hidden></div>
|
||||
<div id="search-results" hidden>
|
||||
<div class="search-results-nav" data-type="top"></div>
|
||||
<div id="search-params">
|
||||
<input id="search-query" type="search" i18n="placeholder:search, title:searchStyleQueryHint">
|
||||
<div class="select-resizer">
|
||||
<select id="search-order" i18n="title:sortStylesHelpTitle">
|
||||
<option value="n" i18n="genericTitle">
|
||||
<option value="u" i18n="searchResultUpdated">
|
||||
<option value="t" i18n="searchResultInstallCount">
|
||||
<option value="w" i18n="searchResultWeeklyCount">
|
||||
<option value="r" i18n="searchResultRating">
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</div>
|
||||
<label class="checkbox-wrapper" i18n="searchGlobalStyles">
|
||||
<input id="search-globals" type="checkbox" checked>
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
</label>
|
||||
</div>
|
||||
<div id="search-results-list"></div>
|
||||
<div class="search-results-nav" data-type="bottom"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template data-id="searchNav">
|
||||
<div>
|
||||
<button data-type="prev" i18n="title:paginationPrevious" disabled>◄</button>
|
||||
<label>
|
||||
<span data-type="page" i18n="title:paginationCurrent">-</span>
|
||||
/
|
||||
<span data-type="total" i18n="title:paginationEstimated">-</span>
|
||||
</label>
|
||||
<button data-type="next" i18n="title:paginationNext" disabled>►</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template data-id="searchResult">
|
||||
<div class="search-result">
|
||||
<a class="search-result-title"><span></span></a>
|
||||
<div class="search-result-info">
|
||||
<img class="search-result-screenshot" i18n="title:installButton">
|
||||
<div class="search-result-status"></div>
|
||||
<div class="search-result-actions">
|
||||
<button class="search-result-install" i18n="installButton"></button>
|
||||
<button class="search-result-uninstall" i18n="deleteStyleLabel"></button>
|
||||
<button class="search-result-customize" i18n="configureStyle"></button>
|
||||
</div>
|
||||
<dl class="search-result-meta">
|
||||
<div data-type="author">
|
||||
<dt i18n="author"></dt>
|
||||
<dd><a target="_blank" i18n="title:author"></a></dd>
|
||||
</div>
|
||||
<div data-type="rating">
|
||||
<dt i18n="searchResultRating"></dt>
|
||||
<dd i18n="title:searchResultRating"></dd>
|
||||
</div>
|
||||
<div data-type="updated">
|
||||
<dt i18n="searchResultUpdated"></dt>
|
||||
<dd i18n="title:searchResultUpdated"><time></time></dd>
|
||||
</div>
|
||||
<div data-type="weekly">
|
||||
<dt i18n="searchResultWeeklyCount"></dt>
|
||||
<dd i18n="title:searchResultWeeklyCount"></dd>
|
||||
</div>
|
||||
<div data-type="total">
|
||||
<dt i18n="searchResultInstallCount"></dt>
|
||||
<dd i18n="title:searchResultInstallCount"></dd>
|
||||
</div>
|
||||
</dl>
|
||||
<div class="search-result-description"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template data-id="emptySearchResult">
|
||||
<div class="search-result-empty"></div>
|
||||
</template>
|
||||
|
||||
<template data-id="searchResultNotMatching">
|
||||
<p class="not-matching-explainer"
|
||||
i18n="searchResultNotMatching, title:searchResultNotMatchingNote"></p>
|
||||
</template>
|
|
@ -8,8 +8,6 @@
|
|||
'use strict';
|
||||
|
||||
(() => {
|
||||
require(['/popup/search.css']);
|
||||
|
||||
const RESULT_ID_PREFIX = t.template.searchResult.className + '-';
|
||||
const RESULT_SEL = '.' + t.template.searchResult.className;
|
||||
const INDEX_URL = URLS.usoArchiveRaw[0] + 'search-index.json';
|
||||
|
@ -71,10 +69,10 @@
|
|||
const entry = el.closest(RESULT_SEL);
|
||||
return {entry, result: entry && entry._result};
|
||||
};
|
||||
const $classList = sel => (sel instanceof Node ? sel : $(sel)).classList;
|
||||
const show = sel => $classList(sel).remove('hidden');
|
||||
const hide = sel => $classList(sel).add('hidden');
|
||||
|
||||
Events.searchInline = () => {
|
||||
calcCategory();
|
||||
ready = start();
|
||||
};
|
||||
Events.searchSite = event => {
|
||||
// use a less specific category if the inline search wasn't used yet
|
||||
if (!category) calcCategory({retry: 1});
|
||||
|
@ -94,7 +92,6 @@
|
|||
`/scripts/by-site/${tryURL(tabURL).hostname.replace(/^www\./, '')}?language=css${add('&q=', q)}`;
|
||||
Events.openURLandHide.call({href}, event);
|
||||
};
|
||||
|
||||
$('#search-globals').onchange = function () {
|
||||
searchGlobals = this.checked;
|
||||
ready = ready.then(start);
|
||||
|
@ -165,9 +162,6 @@
|
|||
}
|
||||
});
|
||||
|
||||
calcCategory();
|
||||
ready = start();
|
||||
|
||||
function next() {
|
||||
displayedPage = Math.min(totalPages, displayedPage + 1);
|
||||
scrollToFirstResult = true;
|
||||
|
@ -182,15 +176,15 @@
|
|||
|
||||
function error(reason) {
|
||||
dom.error.textContent = reason;
|
||||
show(dom.error);
|
||||
hide(dom.list);
|
||||
dom.error.hidden = false;
|
||||
dom.list.hidden = true;
|
||||
if (dom.error.getBoundingClientRect().bottom < 0) {
|
||||
dom.error.scrollIntoView({behavior: 'smooth', block: 'start'});
|
||||
}
|
||||
}
|
||||
|
||||
async function start() {
|
||||
resetUI.timer = setTimeout(resetUI, 500);
|
||||
resetUI.timer = resetUI.timer || setTimeout(resetUI, 500);
|
||||
try {
|
||||
results = [];
|
||||
for (let retry = 0; !results.length && retry <= 2; retry++) {
|
||||
|
@ -202,22 +196,24 @@
|
|||
results = results.filter(r => !allSupportedIds.has(r.i));
|
||||
}
|
||||
render();
|
||||
(results.length ? show : hide)(dom.list);
|
||||
dom.list.hidden = !results.length;
|
||||
if (!results.length && !$('#search-query').value) {
|
||||
error(t('searchResultNoneFound'));
|
||||
} else {
|
||||
resetUI();
|
||||
}
|
||||
} catch (reason) {
|
||||
error(reason);
|
||||
}
|
||||
clearTimeout(resetUI.timer);
|
||||
resetUI();
|
||||
resetUI.timer = 0;
|
||||
}
|
||||
|
||||
function resetUI() {
|
||||
document.body.classList.add('search-results-shown');
|
||||
show(dom.container);
|
||||
show(dom.list);
|
||||
hide(dom.error);
|
||||
dom.container.hidden = false;
|
||||
dom.list.hidden = false;
|
||||
dom.error.hidden = true;
|
||||
}
|
||||
|
||||
function render() {
|
||||
|
|
Loading…
Reference in New Issue
Block a user