diff --git a/manage.html b/manage.html index df297698..8b7dfa14 100644 --- a/manage.html +++ b/manage.html @@ -195,6 +195,7 @@ + @@ -429,21 +430,32 @@ - + + + + + + + + + + - - - Export - - + + + Export to: + + + + + + + + + + - - - - - - - - - - - - @@ -543,20 +548,7 @@ - - - - - - - - - - - - - diff --git a/manage/bulk-actions.js b/manage/bulk-actions.js new file mode 100644 index 00000000..e68bd07b --- /dev/null +++ b/manage/bulk-actions.js @@ -0,0 +1,121 @@ +/* global $ $$ handleEvent installed exportToFile checkUpdateAll */ +/* exported bulk */ +'use strict'; + +const bulk = { + + init: () => { + document.addEventListener('change', bulk.updateBulkFilters); + $('#bulk-actions-select').onchange = bulk.handleSelect; + $('#bulk-actions-apply').onclick = bulk.handleApply; + }, + + handleSelect: event => { + event.preventDefault(); + $$('[data-bulk]').forEach(el => el.classList.add('hidden')); +console.log('select', this.value) + switch (event.target.value) { + case 'enable': + break; + case 'disable': + break; + case 'export': + console.log('got here') + $('[data-bulk="export"]').classList.remove('hidden'); + break; + case 'update': + $('[data-bulk="update"]').classList.remove('hidden'); + break; + case 'reset': + break; + case 'delete': + break; + } + }, + + handleApply: event => { + event.preventDefault(); + let styles; + const action = $('#bulk-actions-select').value; + const entries = $$('.entry-filter-toggle:checked').map(el => el.closest('.entry')); + + switch (action) { + case 'enable': + case 'disable': { + const isEnabled = action === 'enable'; + entries.forEach(entry => { + const box = $('.entry-state-toggle', entry); + entry.classList.toggle('enable', isEnabled); + box.checked = isEnabled; + handleEvent.toggle.call(box, event, entry); + }); + break; + } + case 'export': + styles = entries.map(entry => entry.styleMeta); + exportToFile(styles); + break; + case 'update': + checkUpdateAll(entries); // TO DO + break; + case 'reset': + break; + case 'delete': + styles = entries.reduce((acc, entry) => { + const style = entry.styleMeta; + acc[style.name] = style.id; + return acc; + }, {}); + bulk.deleteBulk(event, styles); + $('#toggle-all-filters').checked = false; + break; + } + $('#bulk-actions-select').value = ''; + }, + + updateBulkFilters: ({target}) => { + // total is undefined until initialized + if (installed.dataset.total) { + // ignore filter checkboxes + if (target.type === 'checkbox' && !target.dataset.filter && target.closest('#tools-wrapper, .entry')) { + handleEvent.toggleBulkActions({hidden: false}); + const bulk = $('#toggle-all-filters'); + const state = target.checked; + const visibleEntries = $$('.entry-filter-toggle') + .filter(entry => !entry.closest('.entry').classList.contains('hidden')); + bulk.indeterminate = false; + if (target === bulk) { + visibleEntries.forEach(entry => { + entry.checked = state; + }); + } else { + if (visibleEntries.length === visibleEntries.filter(entry => entry.checked === state).length) { + bulk.checked = state; + } else { + bulk.checked = false; + bulk.indeterminate = true; + } + } + } + const count = $$('.entry-filter-toggle').filter(entry => entry.checked).length; + $('#bulk-filter-count').textContent = count || ''; + } + }, + + deleteBulk: (event, styles) => { + messageBox({ + title: t('deleteStyleConfirm'), + contents: Object.keys(styles).join(', '), + className: 'danger center', + buttons: [t('confirmDelete'), t('confirmCancel')], + }) + .then(({button}) => { + if (button === 0) { + Object.values(styles).forEach(id => API.deleteStyle(id)); + installed.dataset.total -= Object.keys(styles).length; + bulk.updateBulkFilters({target: $('#toggle-all-filters')}); + } + }); + } + +} diff --git a/manage/import-export.js b/manage/import-export.js index 18abd480..a3f8f922 100644 --- a/manage/import-export.js +++ b/manage/import-export.js @@ -294,38 +294,36 @@ function importFromString(jsonString) { } -function exportToFile() { - API.getAllStyles().then(styles => { - // https://crbug.com/714373 - document.documentElement.appendChild( - $create('iframe', { - onload() { - const text = JSON.stringify(styles, null, '\t'); - const type = 'application/json'; - this.onload = null; - this.contentDocument.body.appendChild( - $create('a', { - href: URL.createObjectURL(new Blob([text], {type})), - download: generateFileName(), - type, - }) - ).dispatchEvent(new MouseEvent('click')); - }, - // we can't use display:none as some browsers are ignoring such iframes - style: ` - all: unset; - width: 0; - height: 0; - position: fixed; - opacity: 0; - border: none; - `.replace(/;/g, '!important;'), - }) - ); +function exportToFile(styles) { + // https://crbug.com/714373 + document.documentElement.appendChild( + $create('iframe', { + onload() { + const text = JSON.stringify(styles, null, '\t'); + const type = 'application/json'; + this.onload = null; + this.contentDocument.body.appendChild( + $create('a', { + href: URL.createObjectURL(new Blob([text], {type})), + download: generateFileName(), + type, + }) + ).dispatchEvent(new MouseEvent('click')); + }, + // we can't use display:none as some browsers are ignoring such iframes + style: ` + all: unset; + width: 0; + height: 0; + position: fixed; + opacity: 0; + border: none; + `.replace(/;/g, '!important;'), + }) + ); // we don't remove the iframe or the object URL because the browser may show // a download dialog and we don't know how long it'll take until the user confirms it // (some browsers like Vivaldi can't download if we revoke the URL) - }); function generateFileName() { const today = new Date(); diff --git a/manage/manage-actions.js b/manage/manage-actions.js index ae7b8cb6..47a1d368 100644 --- a/manage/manage-actions.js +++ b/manage/manage-actions.js @@ -89,8 +89,6 @@ function initGlobalEvents() { } }); - document.addEventListener('change', updateBulkFilters); - $$('[data-toggle-on-click]').forEach(el => { // dataset on SVG doesn't work in Chrome 49-??, works in 57+ const target = $(el.getAttribute('data-toggle-on-click')); @@ -110,6 +108,7 @@ function initGlobalEvents() { // N.B. triggers existing onchange listeners setupLivePrefs(); + bulk.init(); sorter.init(); prefs.subscribe([ @@ -467,34 +466,6 @@ function onVisibilityChange() { } } -function updateBulkFilters({target}) { - // total is undefined until initialized - if (!installed.dataset.total) return; - // ignore filter checkboxes - if (target.type === 'checkbox' && !target.dataset.filter && target.closest('#tools-wrapper, .entry')) { - handleEvent.toggleBulkActions({hidden: false}); - const bulk = $('#toggle-all-filters'); - const state = target.checked; - const visibleEntries = $$('.entry-filter-toggle') - .filter(entry => !entry.closest('.entry').classList.contains('hidden')); - bulk.indeterminate = false; - if (target === bulk) { - visibleEntries.forEach(entry => { - entry.checked = state; - }); - } else { - if (visibleEntries.length === visibleEntries.filter(entry => entry.checked === state).length) { - bulk.checked = state; - } else { - bulk.checked = false; - bulk.indeterminate = true; - } - } - const count = $$('.entry-filter-toggle').filter(entry => entry.checked).length; - $('#bulk-filter-count').textContent = count || ''; - } -} - function removeSelection() { const sel = window.getSelection ? window.getSelection() : document.selection; if (sel) { diff --git a/manage/manage-ui.js b/manage/manage-ui.js index dd8585c8..abfdb32e 100644 --- a/manage/manage-ui.js +++ b/manage/manage-ui.js @@ -33,15 +33,12 @@ const UI = { // translate CSS manually document.head.appendChild($create('style', ` - .disabled h2::after { - content: "${t('genericDisabledLabel')}"; + body.all-styles-hidden-by-filters #installed:after { + content: "${t('filteredStylesAllHidden')}"; } #update-all-no-updates[data-skipped-edited="true"]::after { content: " ${t('updateAllCheckSucceededSomeEdited')}"; } - body.all-styles-hidden-by-filters::after { - content: "${t('filteredStylesAllHidden')}"; - } `)); }, diff --git a/manage/manage.css b/manage/manage.css index 34e53f31..99f076ca 100644 --- a/manage/manage.css +++ b/manage/manage.css @@ -73,6 +73,7 @@ a:hover { .entry-actions > a, .bulk-actions-select-wrapper, #bulk-actions-apply { + position: relative; margin-left: 2px; } @@ -157,6 +158,13 @@ a:hover { margin-bottom: 10px; } +body.all-styles-hidden-by-filters #installed:after { + font-size: 20px; + position: relative; + top: 20px; + left: 30px; +} + .entry-header { margin: 0; padding: 0 4px; @@ -451,18 +459,20 @@ a svg, .svg-icon.sort { fill: red; } -.updater-icons > :not(.check-update):after { +.updater-icons > .update:after { content: attr(data-title); border: 1px solid gold; background-color: goldenrod; } +.entry-actions .entry-delete:after, .update-problem .check-update:after { - background-color: red; - border: 1px solid #d40000; + background-color: #d40000; + border: 1px solid red; color: white; } + .update-problem.showTip { animation: fadeout 10s; animation-fill-mode: both; diff --git a/manage/tooltips.css b/manage/tooltips.css index 59f9a2ba..a9743909 100644 --- a/manage/tooltips.css +++ b/manage/tooltips.css @@ -2,6 +2,8 @@ --tooltip-bkgd: #333; --tooltip-border: #555; --tooltip-text: #fff; + --tooltip-error: #d40000; + --tooltip-warn: goldenrod; } [data-title] { @@ -150,3 +152,13 @@ right: -7px; top: 50% } + +.can-update .updater-icons .update:before { + border-right-color: var(--tooltip-warn); +} + +.entry-actions .entry-delete:before, +.update-problem .check-update:before { + border-right-color: var(--tooltip-error); +} + diff --git a/manage/updater-ui.js b/manage/updater-ui.js index d8cf8ed9..94ea9f06 100644 --- a/manage/updater-ui.js +++ b/manage/updater-ui.js @@ -4,7 +4,7 @@ 'use strict'; onDOMready().then(() => { - $('#check-all-updates').onclick = checkUpdateAll; + // $('#check-all-updates').onclick = checkUpdateAll; $('#check-all-updates-force').onclick = checkUpdateAll; $('#apply-all-updates').onclick = applyUpdateAll; $('#update-history').onclick = showUpdateHistory; @@ -29,11 +29,11 @@ function applyUpdateAll() { function checkUpdateAll() { document.body.classList.add('update-in-progress'); - const btnCheck = $('#check-all-updates'); + // const btnCheck = $('#check-all-updates'); const btnCheckForce = $('#check-all-updates-force'); const btnApply = $('#apply-all-updates'); const noUpdates = $('#update-all-no-updates'); - btnCheck.disabled = true; + // btnCheck.disabled = true; btnCheckForce.classList.add('hidden'); btnApply.classList.add('hidden'); noUpdates.classList.add('hidden'); @@ -82,7 +82,7 @@ function checkUpdateAll() { port.onMessage.removeListener(observer); document.body.classList.remove('update-in-progress'); - btnCheck.disabled = total === 0; + // btnCheck.disabled = total === 0; btnApply.disabled = false; renderUpdatesOnlyFilter({check: updated + skippedEdited > 0}); if (!updated) {