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",
"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": {
"message": "Disabled",
"description": "Used in various lists/options to indicate that something is disabled"
@ -726,13 +730,25 @@
"message": "Use /re/ syntax for regexp search",
"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": {
"message": "Rating",
"description": "Text for label that shows the search result's rating"
},
"searchResultInstallCount": {
"message": "Installs",
"description": "Text for label that shows the number of times a search result was installed"
"searchResultUpdated": {
"message": "Updated",
"description": "Text for label that shows the search result's last update date"
},
"searchStyles": {
"message": "Search contents",

View File

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

View File

@ -1,11 +1,24 @@
body.search-results-shown {
overflow-y: scroll;
overflow-y: auto;
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 {
position: relative;
margin-top: -1em;
}
#search-results-error {
@ -18,13 +31,13 @@ body.search-results-shown {
#search-results-list {
position: relative;
min-height: 200px;
min-height: 224px;
}
.search-result,
.search-result-empty {
position: relative;
padding: 8px 8px 24px;
padding: 8px 8px 21px;
min-height: 160px;
}
@ -66,12 +79,12 @@ body.search-results-shown {
.search-result-title {
margin-bottom: .5em;
display: block;
color: #666;
color: #555;
overflow-wrap: break-word;
}
.search-result-title span {
font-size: 1.2em;
font-size: 12px;
font-weight: 600;
}
@ -83,47 +96,56 @@ body.search-results-shown {
position: relative;
}
.search-result-info > div {
/* opacity: 0; */
position: absolute;
width: 100%;
transition: opacity .25s ease-in-out;
}
.search-result:hover .actions {
.search-result:hover .search-result-actions {
opacity: 1;
}
.search-result-overlay {
bottom: -24px;
}
.search-result-info > .actions {
bottom: 15px;
.search-result-actions {
bottom: 20px;
text-align: center;
z-index: 10;
opacity: 0;
position: absolute;
width: 100%;
transition: opacity .5s;
}
.search-result-info > .actions > button {
#search-results .search-result-actions button {
background-color: #fff;
box-shadow: 2px 2px 20px #000;
white-space: nowrap;
margin: 3px;
}
.search-result-info > .actions > button:hover {
background-color: #ccc;
}
.search-result-overlay > * {
.search-result-meta {
background-color: hsla(0, 0%, 93%, 0.75);
display: flex;
justify-content: space-between;
flex-wrap: wrap;
bottom: 0;
position: absolute;
width: 100%;
line-height: 16px;
margin: 0;
}
.search-result-author {
max-width: 20%;
.search-result:hover .search-result-meta {
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;
font-weight: bold;
white-space: nowrap;
@ -132,107 +154,82 @@ body.search-results-shown {
display: block;
}
.search-result-description-group {
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 {
.search-result-meta [data-type="rating"] dd {
text-align: center;
font-weight: bold;
}
.search-result-meta-install-count {
text-align: right;
}
.search-result-rating {
font-weight: 600;
padding: 1px 2px 1px 2px;
}
.search-result-rating.good {
.search-result-meta [data-type="rating"][data-class="good"] dd {
color: darkgreen;
}
.search-result-rating.okay {
color: chocolate;
.search-result-meta [data-type="rating"][data-class="okay"] dd {
color: darkgreen;
}
.search-result-rating.bad {
.search-result-meta [data-type="rating"][data-class="bad"] dd {
color: darkred;
}
.search-result-rating.none {
.search-result-meta [data-type="rating"][data-class="none"] dd {
}
.search-result-install-count {
font-weight: 600;
.search-result-meta [data-type="weekly"],
.search-result-meta [data-type="total"] {
text-align: right;
}
.search-result-install, .search-result-customize, .search-result-uninstall {
margin: 3px;
.search-result-meta [data-type="weekly"] dd,
.search-result-meta [data-type="total"] dd {
font-weight: bold;
}
#search-results-nav-top,
#search-results-nav-bottom {
.search-result-description {
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;
text-align: center;
word-break: keep-all;
}
#search-results-nav-top {
padding-top: 4px;
margin-bottom: 8px;
.search-results-nav[data-type="top"] {
padding-top: 1em;
margin-bottom: 1em;
}
#search-results-nav-bottom {
margin-top: -14px;
margin-bottom: 16px;
.search-results-nav[data-type="bottom"] {
margin-top: -1em;
margin-bottom: 1em;
}
#search-results-nav-top button,
#search-results-nav-bottom button {
-webkit-appearance: none;
#search-results .search-results-nav button {
background: none;
border: none;
padding: .25rem 1rem;
margin: 0 .5rem;
color: #eee;
text-shadow: 0 1px 3px rgba(0, 0, 0, .5);
font-size: 250%;
font-size: 200%;
line-height: 24px;
vertical-align: middle;
cursor: pointer;
}
#search-results-nav-top button:hover,
#search-results-nav-bottom button:hover {
text-shadow: 0 1px 3px #000;
#search-results .search-results-nav button:disabled {
cursor: auto;
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 {

View File

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