diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 2385fdbb..451da03b 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -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", diff --git a/popup.html b/popup.html index 3ab9a22e..d90375fd 100644 --- a/popup.html +++ b/popup.html @@ -1,3 +1,4 @@ + @@ -88,53 +89,49 @@
-
- +
+
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
+
+
+
+
-
-
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -219,10 +216,10 @@
diff --git a/popup/search-results.css b/popup/search-results.css index 70746ff0..0e1f4403 100755 --- a/popup/search-results.css +++ b/popup/search-results.css @@ -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 { diff --git a/popup/search-results.js b/popup/search-results.js index 69cdc6ab..8d556a08 100755 --- a/popup/search-results.js +++ b/popup/search-results.js @@ -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; - } });