show action hints on preview-less search results (#1313)
+ use CSS to control visibility of action buttons + avoid mutating DOM unnecessarily in toggleDataset()
This commit is contained in:
parent
07291f9486
commit
9722554b3b
|
@ -337,10 +337,11 @@ async function showSpinner(parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleDataset(el, prop, state) {
|
function toggleDataset(el, prop, state) {
|
||||||
|
const wasEnabled = el.dataset[prop] != null; // avoids mutating DOM unnecessarily
|
||||||
if (state) {
|
if (state) {
|
||||||
el.dataset[prop] = '';
|
if (!wasEnabled) el.dataset[prop] = '';
|
||||||
} else {
|
} else {
|
||||||
delete el.dataset[prop];
|
if (wasEnabled) delete el.dataset[prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,9 +104,9 @@
|
||||||
<img class="search-result-screenshot" i18n-title="installButton">
|
<img class="search-result-screenshot" i18n-title="installButton">
|
||||||
<div class="search-result-status"></div>
|
<div class="search-result-status"></div>
|
||||||
<div class="search-result-actions">
|
<div class="search-result-actions">
|
||||||
<button class="search-result-install hidden" i18n-text="installButton"></button>
|
<button class="search-result-install" i18n-text="installButton"></button>
|
||||||
<button class="search-result-uninstall hidden" i18n-text="deleteStyleLabel"></button>
|
<button class="search-result-uninstall" i18n-text="deleteStyleLabel"></button>
|
||||||
<button class="search-result-customize hidden" i18n-text="configureStyle"></button>
|
<button class="search-result-customize" i18n-text="configureStyle"></button>
|
||||||
</div>
|
</div>
|
||||||
<dl class="search-result-meta">
|
<dl class="search-result-meta">
|
||||||
<div data-type="author">
|
<div data-type="author">
|
||||||
|
|
|
@ -102,7 +102,7 @@ body.search-results-shown {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result[data-installed] .search-result-status {
|
.search-result .search-result-status {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -114,9 +114,16 @@ body.search-results-shown {
|
||||||
transition: background-color .5s;
|
transition: background-color .5s;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
.search-result[data-no-image] .search-result-status {
|
||||||
|
line-height: 140px;
|
||||||
|
}
|
||||||
|
.search-result[data-no-image]:not([data-installed]) .search-result-status {
|
||||||
|
background-color: rgba(128, 128, 128, .1);
|
||||||
|
color: currentColor;
|
||||||
|
}
|
||||||
.search-result-screenshot:hover ~ .search-result-status {
|
.search-result-screenshot:hover ~ .search-result-status {
|
||||||
background-color: hsla(180, 100%, 27%, 1);
|
background-color: hsla(180, 100%, 27%, 1);
|
||||||
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-actions {
|
.search-result-actions {
|
||||||
|
@ -131,16 +138,26 @@ body.search-results-shown {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity .5s;
|
transition: opacity .5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result:not([data-installed]):hover .search-result-actions {
|
.search-result:not([data-installed]):hover .search-result-actions {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-actions button {
|
.search-result-actions button {
|
||||||
box-shadow: 2px 2px 20px #000;
|
box-shadow: 2px 2px 20px #000;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
}
|
}
|
||||||
|
.search-result[data-no-image] .search-result-actions button {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.search-result-install,
|
||||||
|
.search-result-uninstall,
|
||||||
|
.search-result:not([data-installed]) .search-result-customize,
|
||||||
|
.search-result:not([data-customizable]) .search-result-customize {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.search-result.not-matching .search-result-customize {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
|
||||||
.search-result-meta {
|
.search-result-meta {
|
||||||
background-color: hsla(0, 0%, 93%, 0.75);
|
background-color: hsla(0, 0%, 93%, 0.75);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* global $ $$ $create $remove showSpinner */// dom.js
|
/* global $ $$ $create $remove showSpinner toggleDataset */// dom.js
|
||||||
/* global $entry tabURL */// popup.js
|
/* global $entry tabURL */// popup.js
|
||||||
/* global API */// msg.js
|
/* global API */// msg.js
|
||||||
/* global Events */
|
/* global Events */
|
||||||
|
@ -10,7 +10,8 @@
|
||||||
(() => {
|
(() => {
|
||||||
require(['/popup/search.css']);
|
require(['/popup/search.css']);
|
||||||
|
|
||||||
const RESULT_ID_PREFIX = 'search-result-';
|
const RESULT_ID_PREFIX = t.template.searchResult.className + '-';
|
||||||
|
const RESULT_SEL = '.' + t.template.searchResult.className;
|
||||||
const INDEX_URL = URLS.usoArchiveRaw[0] + 'search-index.json';
|
const INDEX_URL = URLS.usoArchiveRaw[0] + 'search-index.json';
|
||||||
const USW_INDEX_URL = URLS.usw + 'api/index/uso-format';
|
const USW_INDEX_URL = URLS.usw + 'api/index/uso-format';
|
||||||
const USW_ICON = $create('img', {
|
const USW_ICON = $create('img', {
|
||||||
|
@ -39,6 +40,11 @@
|
||||||
* @prop {string} an - authorName
|
* @prop {string} an - authorName
|
||||||
* @prop {string} sn - screenshotName
|
* @prop {string} sn - screenshotName
|
||||||
* @prop {boolean} sa - screenshotArchived
|
* @prop {boolean} sa - screenshotArchived
|
||||||
|
* --------------------- Stylus' internally added extras
|
||||||
|
* @prop {boolean} isUsw
|
||||||
|
* @prop {boolean} installed
|
||||||
|
* @prop {number} installedStyleId
|
||||||
|
* @prop {number} pingbackTimer
|
||||||
*/
|
*/
|
||||||
/** @type IndexEntry[] */
|
/** @type IndexEntry[] */
|
||||||
let results;
|
let results;
|
||||||
|
@ -61,9 +67,14 @@
|
||||||
onload: () => (imgType = '.webp'),
|
onload: () => (imgType = '.webp'),
|
||||||
});
|
});
|
||||||
|
|
||||||
const $class = sel => (sel instanceof Node ? sel : $(sel)).classList;
|
/** @returns {{result: IndexEntry, entry: HTMLElement}} */
|
||||||
const show = sel => $class(sel).remove('hidden');
|
const $resultEntry = el => {
|
||||||
const hide = sel => $class(sel).add('hidden');
|
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');
|
||||||
|
|
||||||
Object.assign(Events, {
|
Object.assign(Events, {
|
||||||
/**
|
/**
|
||||||
|
@ -215,7 +226,7 @@
|
||||||
// keep rendered elements with ids in the range of interest
|
// keep rendered elements with ids in the range of interest
|
||||||
while (
|
while (
|
||||||
plantAt < PAGE_LENGTH &&
|
plantAt < PAGE_LENGTH &&
|
||||||
slot && slot.id === 'search-result-' + (results[start] || {}).i
|
slot && slot.id === RESULT_ID_PREFIX + (results[start] || {}).i
|
||||||
) {
|
) {
|
||||||
slot = slot.nextElementSibling;
|
slot = slot.nextElementSibling;
|
||||||
plantAt++;
|
plantAt++;
|
||||||
|
@ -292,19 +303,22 @@
|
||||||
t.breakWord(name.length < 300 ? name : name.slice(0, 300) + '...');
|
t.breakWord(name.length < 300 ? name : name.slice(0, 300) + '...');
|
||||||
// screenshot
|
// screenshot
|
||||||
const elShot = $('.search-result-screenshot', entry);
|
const elShot = $('.search-result-screenshot', entry);
|
||||||
|
let shotSrc;
|
||||||
if (isUsw) {
|
if (isUsw) {
|
||||||
elShot.src = !/^https?:/i.test(shot) ? BLANK_PIXEL :
|
shotSrc = /^https?:/i.test(shot) && shot.replace(/\.jpg$/, imgType);
|
||||||
imgType !== '.jpg' ? shot.replace(/\.jpg$/, imgType) :
|
|
||||||
shot;
|
|
||||||
} else {
|
} else {
|
||||||
const auto = URLS.uso + `auto_style_screenshots/${id}${USO_AUTO_PIC_SUFFIX}`;
|
elShot._src = URLS.uso + `auto_style_screenshots/${id}${USO_AUTO_PIC_SUFFIX}`;
|
||||||
Object.assign(elShot, {
|
shotSrc = shot && !shot.endsWith(USO_AUTO_PIC_SUFFIX)
|
||||||
src: shot && !shot.endsWith(USO_AUTO_PIC_SUFFIX)
|
? `${shotArchived ? URLS.usoArchiveRaw[0] : URLS.uso + 'style_'}screenshots/${shot}`
|
||||||
? `${shotArchived ? URLS.usoArchiveRaw[0] : URLS.uso + 'style_'}screenshots/${shot}`
|
: elShot._src;
|
||||||
: auto,
|
}
|
||||||
_src: auto,
|
if (shotSrc) {
|
||||||
onerror: fixScreenshot,
|
elShot._entry = entry;
|
||||||
});
|
elShot.src = shotSrc;
|
||||||
|
elShot.onerror = fixScreenshot;
|
||||||
|
} else {
|
||||||
|
elShot.src = BLANK_PIXEL;
|
||||||
|
entry.dataset.noImage = '';
|
||||||
}
|
}
|
||||||
// author
|
// author
|
||||||
Object.assign($('[data-type="author"] a', entry), {
|
Object.assign($('[data-type="author"] a', entry), {
|
||||||
|
@ -350,8 +364,10 @@
|
||||||
this.src = _src;
|
this.src = _src;
|
||||||
delete this._src;
|
delete this._src;
|
||||||
} else {
|
} else {
|
||||||
this.src = BLANK_PIXEL;
|
|
||||||
this.onerror = null;
|
this.onerror = null;
|
||||||
|
this.src = BLANK_PIXEL;
|
||||||
|
this._entry.dataset.noImage = '';
|
||||||
|
renderActionButtons(this._entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,14 +382,10 @@
|
||||||
result.installedStyleId = installedId;
|
result.installedStyleId = installedId;
|
||||||
}
|
}
|
||||||
const isInstalled = result.installed;
|
const isInstalled = result.installed;
|
||||||
if (isInstalled && !('installed' in entry.dataset)) {
|
const status = $('.search-result-status', entry).textContent =
|
||||||
entry.dataset.installed = '';
|
isInstalled ? t('clickToUninstall') :
|
||||||
$('.search-result-status', entry).textContent = t('clickToUninstall');
|
entry.dataset.noImage != null ? t('installButton') :
|
||||||
} else if (!isInstalled && 'installed' in entry.dataset) {
|
'';
|
||||||
delete entry.dataset.installed;
|
|
||||||
$('.search-result-status', entry).textContent = '';
|
|
||||||
hide('.search-result-customize', entry);
|
|
||||||
}
|
|
||||||
const notMatching = installedId > 0 && !$entry(installedId);
|
const notMatching = installedId > 0 && !$entry(installedId);
|
||||||
if (notMatching !== entry.classList.contains('not-matching')) {
|
if (notMatching !== entry.classList.contains('not-matching')) {
|
||||||
entry.classList.toggle('not-matching');
|
entry.classList.toggle('not-matching');
|
||||||
|
@ -385,10 +397,15 @@
|
||||||
}
|
}
|
||||||
Object.assign($('.search-result-screenshot', entry), {
|
Object.assign($('.search-result-screenshot', entry), {
|
||||||
onclick: isInstalled ? uninstall : install,
|
onclick: isInstalled ? uninstall : install,
|
||||||
title: isInstalled ? '' : t('installButton'),
|
title: status ? '' : t('installButton'),
|
||||||
});
|
});
|
||||||
$('.search-result-uninstall', entry).onclick = uninstall;
|
$('.search-result-uninstall', entry).onclick = uninstall;
|
||||||
$('.search-result-install', entry).onclick = install;
|
$('.search-result-install', entry).onclick = install;
|
||||||
|
Object.assign($('.search-result-customize', entry), {
|
||||||
|
onclick: configure,
|
||||||
|
disabled: notMatching,
|
||||||
|
});
|
||||||
|
toggleDataset(entry, 'installed', isInstalled);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFullInfo(entry, style) {
|
function renderFullInfo(entry, style) {
|
||||||
|
@ -402,17 +419,16 @@
|
||||||
textContent: description,
|
textContent: description,
|
||||||
title: description,
|
title: description,
|
||||||
});
|
});
|
||||||
// config button
|
toggleDataset(entry, 'customizable', vars);
|
||||||
if (vars) {
|
}
|
||||||
const btn = $('.search-result-customize', entry);
|
|
||||||
btn.onclick = () => $('.configure', $entry(style)).click();
|
function configure() {
|
||||||
show(btn);
|
const styleEntry = $entry($resultEntry(this).result.installedStyleId);
|
||||||
}
|
Events.configure.call(this, {target: styleEntry});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function install() {
|
async function install() {
|
||||||
const entry = this.closest('.search-result');
|
const {entry, result} = $resultEntry(this);
|
||||||
const result = /** @type IndexEntry */ entry._result;
|
|
||||||
const {i: id, isUsw} = result;
|
const {i: id, isUsw} = result;
|
||||||
const installButton = $('.search-result-install', entry);
|
const installButton = $('.search-result-install', entry);
|
||||||
|
|
||||||
|
@ -443,9 +459,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function uninstall() {
|
function uninstall() {
|
||||||
const entry = this.closest('.search-result');
|
const {entry, result} = $resultEntry(this);
|
||||||
saveScrollPosition(entry);
|
saveScrollPosition(entry);
|
||||||
API.styles.delete(entry._result.installedStyleId);
|
API.styles.delete(result.installedStyleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveScrollPosition(entry) {
|
function saveScrollPosition(entry) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user