simplify inline search DOM structure

This commit is contained in:
tophf 2017-12-11 13:03:03 +03:00
parent 3a82a66954
commit 1d9ec09d62
4 changed files with 179 additions and 174 deletions

View File

@ -202,6 +202,10 @@
"message": "Cannot function properly because of a known bug in this version of Firefox: chrome.extension.getBackgroundPage() doesn't return a valid result", "message": "Cannot function properly because of a known bug in this version of Firefox: chrome.extension.getBackgroundPage() doesn't return a valid result",
"description": "Displayed in style manager when unable to connect to the background page" "description": "Displayed in style manager when unable to connect to the background page"
}, },
"genericErrorOccurred": {
"message": "An error occured:",
"description": "Used in various places to indicate some error occured. The error message is shown on the next line."
},
"genericDisabledLabel": { "genericDisabledLabel": {
"message": "Disabled", "message": "Disabled",
"description": "Used in various lists/options to indicate that something is disabled" "description": "Used in various lists/options to indicate that something is disabled"
@ -726,13 +730,25 @@
"message": "Use /re/ syntax for regexp search", "message": "Use /re/ syntax for regexp search",
"description": "Label after the search input field in the editor shown on Ctrl-F" "description": "Label after the search input field in the editor shown on Ctrl-F"
}, },
"searchResultWeeklyCount": {
"message": "Weekly installs",
"description": "Text for label that shows the number of times a search result was installed during last week"
},
"searchResultInstallCount": {
"message": "Total installs",
"description": "Text for label that shows the number of times a search result was installed"
},
"searchResultNoneFound": {
"message": "No styles found for this site.",
"description": "Error text in the popup when inline search didn't find any site-specific styles"
},
"searchResultRating": { "searchResultRating": {
"message": "Rating", "message": "Rating",
"description": "Text for label that shows the search result's rating" "description": "Text for label that shows the search result's rating"
}, },
"searchResultInstallCount": { "searchResultUpdated": {
"message": "Installs", "message": "Updated",
"description": "Text for label that shows the number of times a search result was installed" "description": "Text for label that shows the search result's last update date"
}, },
"searchStyles": { "searchStyles": {
"message": "Search contents", "message": "Search contents",

View File

@ -1,3 +1,4 @@
<!DOCTYPE html>
<html id="stylus"> <html id="stylus">
<head> <head>
@ -88,53 +89,49 @@
<a class="search-result-title"><span></span></a> <a class="search-result-title"><span></span></a>
<div class="search-result-info"> <div class="search-result-info">
<img class="search-result-screenshot" i18n-title="installButton"> <img class="search-result-screenshot" i18n-title="installButton">
<div class="actions"> <div class="search-result-actions">
<button class="search-result-install" i18n-text="installButton" hidden></button> <button class="search-result-install hidden" i18n-text="installButton"></button>
<button class="search-result-uninstall hidden" i18n-text="deleteStyleLabel"></button> <button class="search-result-uninstall hidden" i18n-text="deleteStyleLabel"></button>
<button class="search-result-customize hidden" <button class="search-result-customize hidden"
i18n-text="configureStyle" i18n-text="configureStyle"
i18n-title="configureStyleOnHomepage"></button> i18n-title="configureStyleOnHomepage"></button>
</div> </div>
<div class="search-result-overlay"> <dl class="search-result-meta">
<div class="search-result-meta"> <div data-type="author">
<div class="search-result-author"> <dt i18n-text="author"></dt>
<span i18n-text="author"></span> <dd><a target="_blank" i18n-title="author"></a></dd>
<a class="search-result-author-link" target="_blank" i18n-title="author"></a>
</div>
<div class="search-result-meta-rating">
<span i18n-text="searchResultRating"></span>
<span class="search-result-rating" i18n-title="searchResultRating"></span>
</div>
<div class="search-result-meta-updated">
<span></span>
<time class="search-result-updated"></time>
</div>
<div class="search-result-meta-weekly-count">
<span i18n-text="searchResultInstallCount"></span>
<span class="search-result-weekly-count" i18n-title="searchResultInstallCount"></span>
</div>
<div class="search-result-meta-install-count">
<span i18n-text="searchResultInstallCount"></span>
<span class="search-result-install-count" i18n-title="searchResultInstallCount"></span>
</div>
</div> </div>
<div class="search-result-description-group"> <div data-type="rating">
<div class="search-result-description"></div> <dt i18n-text="searchResultRating"></dt>
<dd i18n-title="searchResultRating"></dd>
</div> </div>
</div> <div data-type="updated">
<dt i18n-text="searchResultUpdated"></dt>
<dd i18n-title="searchResultUpdated"><time></time></dd>
</div>
<div data-type="weekly">
<dt i18n-text="searchResultWeeklyCount"></dt>
<dd i18n-title="searchResultWeeklyCount"></dd>
</div>
<div data-type="total">
<dt i18n-text="searchResultInstallCount"></dt>
<dd i18n-title="searchResultInstallCount"></dd>
</div>
</dl>
<div class="search-result-description"></div>
</div> </div>
</div> </div>
</template> </template>
<template data-id="searchNav"> <template data-id="searchNav">
<div> <div>
<button data-role="prev" i18n-title="paginationPrevious" disabled></button> <button data-type="prev" i18n-title="paginationPrevious" disabled></button>
<label> <label>
<span data-role="page" i18n-title="paginationCurrent">-</span> <span data-type="page" i18n-title="paginationCurrent">-</span>
/ /
<span data-role="total" i18n-title="paginationTotal">-</span> <span data-type="total" i18n-title="paginationTotal">-</span>
</label> </label>
<button data-role="next" i18n-title="paginationNext" disabled></button> <button data-type="next" i18n-title="paginationNext" disabled></button>
</div> </div>
</template> </template>
@ -219,10 +216,10 @@
</div> </div>
<div id="search-results" class="hidden"> <div id="search-results" class="hidden">
<div id="search-results-nav-top"></div> <div class="search-results-nav" data-type="top"></div>
<div id="search-results-error" class="hidden"></div> <div id="search-results-error" class="hidden"></div>
<div id="search-results-list"></div> <div id="search-results-list"></div>
<div id="search-results-nav-bottom"></div> <div class="search-results-nav" data-type="bottom"></div>
</div> </div>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none !important;"> <svg xmlns="http://www.w3.org/2000/svg" style="display: none !important;">

View File

@ -1,11 +1,24 @@
body.search-results-shown { body.search-results-shown {
overflow-y: scroll; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
background-color: #aaa; }
#search-results:before {
background-image: linear-gradient(transparent, rgba(0, 0, 0, .3) 200px);
content: "";
top: -50px;
left: -1000px;
right: -1000px;
bottom: -12px;
position: absolute;
pointer-events: none;
animation: fadein 1s;
animation-fill-mode: both;
} }
#search-results { #search-results {
position: relative; position: relative;
margin-top: -1em;
} }
#search-results-error { #search-results-error {
@ -18,13 +31,13 @@ body.search-results-shown {
#search-results-list { #search-results-list {
position: relative; position: relative;
min-height: 200px; min-height: 224px;
} }
.search-result, .search-result,
.search-result-empty { .search-result-empty {
position: relative; position: relative;
padding: 8px 8px 24px; padding: 8px 8px 21px;
min-height: 160px; min-height: 160px;
} }
@ -66,12 +79,12 @@ body.search-results-shown {
.search-result-title { .search-result-title {
margin-bottom: .5em; margin-bottom: .5em;
display: block; display: block;
color: #666; color: #555;
overflow-wrap: break-word; overflow-wrap: break-word;
} }
.search-result-title span { .search-result-title span {
font-size: 1.2em; font-size: 12px;
font-weight: 600; font-weight: 600;
} }
@ -83,47 +96,56 @@ body.search-results-shown {
position: relative; position: relative;
} }
.search-result-info > div { .search-result:hover .search-result-actions {
/* opacity: 0; */
position: absolute;
width: 100%;
transition: opacity .25s ease-in-out;
}
.search-result:hover .actions {
opacity: 1; opacity: 1;
} }
.search-result-overlay { .search-result-actions {
bottom: -24px; bottom: 20px;
}
.search-result-info > .actions {
bottom: 15px;
text-align: center; text-align: center;
z-index: 10; z-index: 10;
opacity: 0; opacity: 0;
position: absolute;
width: 100%;
transition: opacity .5s;
} }
.search-result-info > .actions > button { #search-results .search-result-actions button {
background-color: #fff; background-color: #fff;
box-shadow: 2px 2px 20px #000; box-shadow: 2px 2px 20px #000;
white-space: nowrap; white-space: nowrap;
margin: 3px;
} }
.search-result-info > .actions > button:hover { .search-result-meta {
background-color: #ccc; background-color: hsla(0, 0%, 93%, 0.75);
}
.search-result-overlay > * {
display: flex; display: flex;
justify-content: space-between;
flex-wrap: wrap;
bottom: 0;
position: absolute;
width: 100%;
line-height: 16px;
margin: 0;
} }
.search-result-author { .search-result:hover .search-result-meta {
max-width: 20%; background-color: hsla(0, 0%, 100%, 0.75);
} }
.search-result-author-link { .search-result-meta dt {
display: none;
}
.search-result-meta dd {
margin: 0;
}
.search-result-meta [data-type="author"] {
max-width: 30%;
}
.search-result-meta [data-type="author"] a {
color: inherit; color: inherit;
font-weight: bold; font-weight: bold;
white-space: nowrap; white-space: nowrap;
@ -132,107 +154,82 @@ body.search-results-shown {
display: block; display: block;
} }
.search-result-description-group { .search-result-meta [data-type="rating"] dd {
align-items: center;
padding: 0;
font-size: 90%;
line-height: 24px;
min-height: 5px;
}
.search-result-description {
flex: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.search-result-meta {
background-color: hsla(0, 0%, 93%, 0.76);
justify-content: space-between;
flex-wrap: wrap;
}
.search-result:hover .search-result-meta {
background-color: hsla(0, 0%, 100%, 0.76);
}
.search-result-meta > * > :first-child {
display: none;
opacity: .75;
}
.search-result-meta-rating {
text-align: center; text-align: center;
font-weight: bold;
} }
.search-result-meta-install-count { .search-result-meta [data-type="rating"][data-class="good"] dd {
text-align: right;
}
.search-result-rating {
font-weight: 600;
padding: 1px 2px 1px 2px;
}
.search-result-rating.good {
color: darkgreen; color: darkgreen;
} }
.search-result-rating.okay { .search-result-meta [data-type="rating"][data-class="okay"] dd {
color: chocolate; color: darkgreen;
} }
.search-result-rating.bad { .search-result-meta [data-type="rating"][data-class="bad"] dd {
color: darkred; color: darkred;
} }
.search-result-rating.none { .search-result-meta [data-type="rating"][data-class="none"] dd {
} }
.search-result-install-count { .search-result-meta [data-type="weekly"],
font-weight: 600; .search-result-meta [data-type="total"] {
text-align: right;
} }
.search-result-install, .search-result-customize, .search-result-uninstall { .search-result-meta [data-type="weekly"] dd,
margin: 3px; .search-result-meta [data-type="total"] dd {
font-weight: bold;
} }
#search-results-nav-top, .search-result-description {
#search-results-nav-bottom { padding: 0;
font-size: 90%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
position: absolute;
width: 100%;
margin-top: 4px;
}
.search-results-nav {
flex-direction: row; flex-direction: row;
text-align: center; text-align: center;
word-break: keep-all; word-break: keep-all;
} }
#search-results-nav-top { .search-results-nav[data-type="top"] {
padding-top: 4px; padding-top: 1em;
margin-bottom: 8px; margin-bottom: 1em;
} }
#search-results-nav-bottom { .search-results-nav[data-type="bottom"] {
margin-top: -14px; margin-top: -1em;
margin-bottom: 16px; margin-bottom: 1em;
} }
#search-results-nav-top button, #search-results .search-results-nav button {
#search-results-nav-bottom button {
-webkit-appearance: none;
background: none; background: none;
border: none; border: none;
padding: .25rem 1rem; padding: .25rem 1rem;
margin: 0 .5rem; margin: 0 .5rem;
color: #eee; font-size: 200%;
text-shadow: 0 1px 3px rgba(0, 0, 0, .5);
font-size: 250%;
line-height: 24px; line-height: 24px;
vertical-align: middle; vertical-align: middle;
cursor: pointer; cursor: pointer;
} }
#search-results-nav-top button:hover, #search-results .search-results-nav button:disabled {
#search-results-nav-bottom button:hover { cursor: auto;
text-shadow: 0 1px 3px #000; opacity: .5;
pointer-events: none;
}
#search-results .search-results-nav button:not(:disabled):hover {
text-shadow: 0 1px 4px rgba(0, 0, 0, .5);
} }
#find-styles-inline-group label { #find-styles-inline-group label {

View File

@ -98,13 +98,13 @@ window.addEventListener('showStyles:done', function _() {
dom.nav = {}; dom.nav = {};
const navOnClick = {prev, next}; const navOnClick = {prev, next};
for (const place of ['top', 'bottom']) { for (const place of ['top', 'bottom']) {
const nav = $('#search-results-nav-' + place); const nav = $(`.search-results-nav[data-type="${place}"]`);
nav.appendChild(template.searchNav.cloneNode(true)); nav.appendChild(template.searchNav.cloneNode(true));
dom.nav[place] = nav; dom.nav[place] = nav;
for (const child of $$('[data-role]', nav)) { for (const child of $$('[data-type]', nav)) {
const role = child.dataset.role; const type = child.dataset.type;
child.onclick = navOnClick[role]; child.onclick = navOnClick[type];
nav['_' + role] = child; nav['_' + type] = child;
} }
} }
@ -179,14 +179,10 @@ window.addEventListener('showStyles:done', function _() {
* @param {string} message Message to display to user. * @param {string} message Message to display to user.
*/ */
function error(reason) { function error(reason) {
let message; dom.error.textContent =
if (reason === 404) { reason === 404 ?
// TODO: i18n message t('searchResultNoneFound') :
message = 'No results found'; t('genericErrorOccurred') + '\n' + reason;
} else {
message = 'Error loading search results: ' + reason;
}
dom.error.textContent = message;
dom.error.classList.remove('hidden'); dom.error.classList.remove('hidden');
} }
@ -320,13 +316,17 @@ window.addEventListener('showStyles:done', function _() {
showSpinner(entry); showSpinner(entry);
} }
plantAt++; plantAt++;
if (!processedResults.length) {
break;
}
} }
while (dom.list.children.length > maxResults) { while (dom.list.children.length > maxResults) {
dom.list.lastElementChild.remove(); dom.list.lastElementChild.remove();
} }
if (scrollToFirstResult && dom.list.children[0]) { if (scrollToFirstResult &&
dom.container.getBoundingClientRect().bottom > window.innerHeight * 2) {
scrollToFirstResult = false; scrollToFirstResult = false;
if (!FIREFOX || FIREFOX >= 55) { if (!FIREFOX || FIREFOX >= 55) {
setTimeout(() => { setTimeout(() => {
@ -386,22 +386,19 @@ window.addEventListener('showStyles:done', function _() {
} }
const description = result.description const description = result.description
.replace(/<[^>]*>/g, '') .replace(/<[^>]*>/g, ' ')
.replace(/([^.]\.)(\s)/g, '$1\n$2') .replace(/([^.][.。?!]|[\s,].{50,70})\s+/g, '$1\n')
.replace(/[\r\n]{3,}/g, '\n\n'); .replace(/([\r\n]\s*){3,}/g, '\n\n');
Object.assign($('.search-result-description', entry), { Object.assign($('.search-result-description', entry), {
textContent: description, textContent: description,
title: description, title: description,
}); });
Object.assign($('.search-result-author-link', entry), { Object.assign($('[data-type="author"] a', entry), {
textContent: result.user.name, textContent: result.user.name,
title: result.user.name, title: result.user.name,
href: BASE_URL + '/users/' + result.user.id, href: BASE_URL + '/users/' + result.user.id,
onclick(event) { onclick: handleEvent.openURLandHide,
event.stopPropagation();
handleEvent.openURLandHide.call(this, event);
}
}); });
let ratingClass; let ratingClass;
@ -419,11 +416,10 @@ window.addEventListener('showStyles:done', function _() {
ratingClass = 'bad'; ratingClass = 'bad';
ratingValue = ratingValue.toFixed(1); ratingValue = ratingValue.toFixed(1);
} }
Object.assign($('.search-result-rating', entry), { $('[data-type="rating"]', entry).dataset.class = ratingClass;
textContent: ratingValue, $('[data-type="rating"] dd', entry).textContent = ratingValue;
className: 'search-result-rating ' + ratingClass
}); Object.assign($('[data-type="updated"] time', entry), {
Object.assign($('.search-result-meta-updated', entry), {
dateTime: result.updated, dateTime: result.updated,
textContent: tryCatch(lang => { textContent: tryCatch(lang => {
const date = new Date(result.updated); const date = new Date(result.updated);
@ -434,17 +430,26 @@ window.addEventListener('showStyles:done', function _() {
}); });
}, [UI_LANG, 'en']) || '', }, [UI_LANG, 'en']) || '',
}); });
Object.assign($('.search-result-weekly-count', entry), {
textContent: result.weekly_install_count.toLocaleString()
}); $('[data-type="weekly"] dd', entry).textContent = formatNumber(result.weekly_install_count);
Object.assign($('.search-result-install-count', entry), { $('[data-type="total"] dd', entry).textContent = formatNumber(result.total_install_count);
textContent: result.total_install_count.toLocaleString()
});
renderActionButtons(entry); renderActionButtons(entry);
return entry; return entry;
} }
function formatNumber(num) {
return (
num > 1e9 ? (num / 1e9).toFixed(1) + 'B' :
num > 10e6 ? (num / 1e6).toFixed(0) + 'M' :
num > 1e6 ? (num / 1e6).toFixed(1) + 'M' :
num > 10e3 ? (num / 1e3).toFixed(0) + 'k' :
num > 1e3 ? (num / 1e3).toFixed(1) + 'k' :
num
);
}
function renderActionButtons(entry) { function renderActionButtons(entry) {
const screenshot = $('.search-result-screenshot', entry); const screenshot = $('.search-result-screenshot', entry);
screenshot.onclick = entry._result.installed ? onUninstallClicked : onInstallClicked; screenshot.onclick = entry._result.installed ? onUninstallClicked : onInstallClicked;
@ -724,14 +729,4 @@ window.addEventListener('showStyles:done', function _() {
} }
//endregion //endregion
function objectPick(obj, keys) {
const result = {};
for (const k in obj) {
if (keys.includes(k)) {
result[k] = obj[k];
}
}
return result;
}
}); });