Add bulk action panel
This commit is contained in:
parent
5c38441393
commit
fbcc7aac08
206
manage.html
206
manage.html
|
@ -30,7 +30,7 @@
|
|||
<template data-id="style-header">
|
||||
<div class="entry-header">
|
||||
<div class="entry-col header-filter center-text">
|
||||
<a href="#" id="toggle-all">
|
||||
<a href="#" id="toggle-actions">
|
||||
<svg class="svg-icon" width="20" height="20" viewBox="0 0 14 14">
|
||||
<path d="M6.42 7.58L2.92 3.5h8.75l-3.5 4.08v4.09c-1 0-1.75-.76-1.75-1.75V7.58z"/>
|
||||
</svg>
|
||||
|
@ -50,7 +50,7 @@
|
|||
<template data-id="style">
|
||||
<div class="entry">
|
||||
<div class="entry-col entry-filter">
|
||||
<label>
|
||||
<label class="checkmate" tabindex="0">
|
||||
<input class="entry-filter" type="checkbox">
|
||||
<svg class="svg-icon checkbox" viewBox="0 0 24 24">
|
||||
<path class="circle" d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8.01 8.01 0 0 1 0-16 8.01 8.01 0 0 1 0 16z"/>
|
||||
|
@ -60,7 +60,7 @@
|
|||
</div>
|
||||
<div class="entry-col entry-id"></div>
|
||||
<div class="entry-col entry-state">
|
||||
<label>
|
||||
<label class="checkmate" tabindex="0">
|
||||
<input class="entry-state-toggle" type="checkbox">
|
||||
<svg class="svg-icon checkbox" viewBox="0 0 24 24">
|
||||
<path class="circle" d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8.01 8.01 0 0 1 0-16 8.01 8.01 0 0 1 0 16z"/>
|
||||
|
@ -216,6 +216,16 @@
|
|||
</svg>
|
||||
<span class="ext-name">Stylus</span>
|
||||
<span class="ext-version"></span>
|
||||
<span class="filter-stats-wrapper">
|
||||
<span id="filters-stats"></span>
|
||||
<a id="reset-filters" href="#" tabindex="0">
|
||||
<svg class="svg-icon" viewBox="0 0 20 20">
|
||||
<title i18n-text="genericResetLabel"></title>
|
||||
<polygon points="16.2,5.5 14.5,3.8 10,8.3 5.5,3.8 3.8,5.5 8.3,10 3.8,14.5
|
||||
5.5,16.2 10,11.7 14.5,16.2 16.2,14.5 11.7,10 "/>
|
||||
</svg>
|
||||
</a>
|
||||
</span>
|
||||
<span class="tabs">
|
||||
<a href="#" class="active">Manage</a>
|
||||
<a href="#">Options</a>
|
||||
|
@ -223,7 +233,101 @@
|
|||
</span>
|
||||
</h1>
|
||||
|
||||
<div id="manage-bulk-actions" style="display: none">
|
||||
<div id="manage-bulk-actions" class="hidden">
|
||||
<div id="filters" class="manage-row">
|
||||
<span id="search-wrapper">
|
||||
<input id="search" type="search" i18n-placeholder="searchStyles" spellcheck="false"
|
||||
data-filter=":not(.not-matching)"
|
||||
data-filter-hide=".not-matching">
|
||||
<a href="#" id="search-help" tabindex="0">
|
||||
<svg class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg>
|
||||
</a>
|
||||
</span>
|
||||
<strong i18n-text="manageFilters"></strong>
|
||||
<span class="filter-selection">
|
||||
<label class="checkmate" tabindex="0">
|
||||
<input id="manage.onlyEnabled" type="checkbox"
|
||||
data-filter=".enabled"
|
||||
data-filter-hide=".disabled">
|
||||
<svg class="svg-icon checkbox" viewBox="0 0 24 24">
|
||||
<path class="circle" d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8.01 8.01 0 0 1 0-16 8.01 8.01 0 0 1 0 16z"/>
|
||||
<path class="checkmark" d="M16.59 7.52L10 14.11l-2.59-2.58L6 12.94l4 4 8-8z"/>
|
||||
</svg>
|
||||
</label>
|
||||
<span class="select-resizer">
|
||||
<select id="manage.onlyEnabled.invert">
|
||||
<option i18n-text="manageOnlyEnabled" value="false"></option>
|
||||
<option i18n-text="manageOnlyDisabled" value="true"></option>
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="filter-selection">
|
||||
<label class="checkmate" tabindex="0">
|
||||
<input id="manage.onlyLocal" type="checkbox"
|
||||
data-filter=":not(.updatable):not(.update-done)"
|
||||
data-filter-hide=".updatable, .update-done">
|
||||
<svg class="svg-icon checkbox" viewBox="0 0 24 24">
|
||||
<path class="circle" d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8.01 8.01 0 0 1 0-16 8.01 8.01 0 0 1 0 16z"/>
|
||||
<path class="checkmark" d="M16.59 7.52L10 14.11l-2.59-2.58L6 12.94l4 4 8-8z"/>
|
||||
</svg>
|
||||
</label>
|
||||
<span class="select-resizer">
|
||||
<select id="manage.onlyLocal.invert" i18n-title="manageOnlyLocalTooltip">
|
||||
<option i18n-text="manageOnlyLocal" value="false"></option>
|
||||
<option i18n-text="manageOnlyExternal" value="true"></option>
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<span class="filter-selection">
|
||||
<label class="checkmate" tabindex="0">
|
||||
<input id="manage.onlyUsercss" type="checkbox"
|
||||
data-filter=".usercss"
|
||||
data-filter-hide=":not(.usercss)">
|
||||
<svg class="svg-icon checkbox" viewBox="0 0 24 24">
|
||||
<path class="circle" d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8.01 8.01 0 0 1 0-16 8.01 8.01 0 0 1 0 16z"/>
|
||||
<path class="checkmark" d="M16.59 7.52L10 14.11l-2.59-2.58L6 12.94l4 4 8-8z"/>
|
||||
</svg>
|
||||
</label>
|
||||
<span class="select-resizer">
|
||||
<select id="manage.onlyUsercss.invert">
|
||||
<option i18n-text="manageOnlyUsercss" value="false"></option>
|
||||
<option i18n-text="manageOnlyNonUsercss" value="true"></option>
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<label id="only-updates" class="hidden">
|
||||
<label class="checkmate" tabindex="0">
|
||||
<input type="checkbox"
|
||||
data-filter=".can-update, .update-problem, .update-done"
|
||||
data-filter-hide=":not(.updatable):not(.update-done),
|
||||
.no-update:not(.update-problem),
|
||||
.updatable:not(.can-update):not(.update-problem):not(.update-done)">
|
||||
<svg class="svg-icon checkbox" viewBox="0 0 24 24">
|
||||
<path class="circle" d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8.01 8.01 0 0 1 0-16 8.01 8.01 0 0 1 0 16z"/>
|
||||
<path class="checkmark" d="M16.59 7.52L10 14.11l-2.59-2.58L6 12.94l4 4 8-8z"/>
|
||||
</svg>
|
||||
<span i18n-text="manageOnlyUpdates"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="manage-row">
|
||||
<label class="checkmate" tabindex="0">
|
||||
<input class="toggle-all-filters" type="checkbox">
|
||||
<svg class="svg-icon checkbox" viewBox="0 0 24 24">
|
||||
<path class="circle" d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm0 18a8.01 8.01 0 0 1 0-16 8.01 8.01 0 0 1 0 16z"/>
|
||||
<path class="checkmark" d="M16.59 7.52L10 14.11l-2.59-2.58L6 12.94l4 4 8-8z"/>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style="display: none">
|
||||
<!-- placeholders -->
|
||||
<input id="search" type="search">
|
||||
<button id="search-help"></button>
|
||||
<!-- <button id="reset-filters"></button> -->
|
||||
|
@ -243,103 +347,13 @@
|
|||
<input id="manage.newUI.favicons" type="checkbox">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="installed" class="manage-col-entries"></div>
|
||||
|
||||
<!-- <div id="manage-settings">
|
||||
<div class="settings-column">
|
||||
-->
|
||||
<details id="filters" data-pref="manage.filters.expanded" style="display: none">
|
||||
<summary>
|
||||
<h2 i18n-text="manageFilters">:
|
||||
<div class="filter-stats-wrapper">
|
||||
<span id="filters-stats"></span>
|
||||
<a id="reset-filters" href="#" tabindex="0">
|
||||
<svg class="svg-icon" viewBox="0 0 20 20">
|
||||
<title i18n-text="genericResetLabel"></title>
|
||||
<polygon points="16.2,5.5 14.5,3.8 10,8.3 5.5,3.8 3.8,5.5 8.3,10 3.8,14.5
|
||||
5.5,16.2 10,11.7 14.5,16.2 16.2,14.5 11.7,10 "/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</h2>
|
||||
</summary>
|
||||
|
||||
<div class="filter-selection">
|
||||
<label>
|
||||
<div class="checkmate">
|
||||
<input id="manage.onlyEnabled" type="checkbox"
|
||||
data-filter=".enabled"
|
||||
data-filter-hide=".disabled">
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
</div>
|
||||
</label>
|
||||
<div class="select-resizer">
|
||||
<select id="manage.onlyEnabled.invert">
|
||||
<option i18n-text="manageOnlyEnabled" value="false"></option>
|
||||
<option i18n-text="manageOnlyDisabled" value="true"></option>
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-selection">
|
||||
<label>
|
||||
<div class="checkmate">
|
||||
<input id="manage.onlyLocal" type="checkbox"
|
||||
data-filter=":not(.updatable):not(.update-done)"
|
||||
data-filter-hide=".updatable, .update-done">
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
</div>
|
||||
</label>
|
||||
<div class="select-resizer">
|
||||
<select id="manage.onlyLocal.invert" i18n-title="manageOnlyLocalTooltip">
|
||||
<option i18n-text="manageOnlyLocal" value="false"></option>
|
||||
<option i18n-text="manageOnlyExternal" value="true"></option>
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="filter-selection">
|
||||
<label>
|
||||
<div class="checkmate">
|
||||
<input id="manage.onlyUsercss" type="checkbox"
|
||||
data-filter=".usercss"
|
||||
data-filter-hide=":not(.usercss)">
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
</div>
|
||||
</label>
|
||||
<div class="select-resizer">
|
||||
<select id="manage.onlyUsercss.invert">
|
||||
<option i18n-text="manageOnlyUsercss" value="false"></option>
|
||||
<option i18n-text="manageOnlyNonUsercss" value="true"></option>
|
||||
</select>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label id="only-updates" class="hidden">
|
||||
<input type="checkbox"
|
||||
data-filter=".can-update, .update-problem, .update-done"
|
||||
data-filter-hide=":not(.updatable):not(.update-done),
|
||||
.no-update:not(.update-problem),
|
||||
.updatable:not(.can-update):not(.update-problem):not(.update-done)">
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
<span i18n-text="manageOnlyUpdates"></span>
|
||||
</label>
|
||||
|
||||
<div id="search-wrapper">
|
||||
<input id="search" type="search" i18n-placeholder="searchStyles" spellcheck="false"
|
||||
data-filter=":not(.not-matching)"
|
||||
data-filter-hide=".not-matching">
|
||||
<a href="#" id="search-help" tabindex="0">
|
||||
<svg class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</details>
|
||||
|
||||
<!--
|
||||
<div id="sort-wrapper">
|
||||
<div class="sorter-selection" i18n-title="sortLabel">
|
||||
<select id="manage.newUI.sort"></select>
|
||||
|
|
|
@ -101,9 +101,6 @@ function init() {
|
|||
|
||||
$$('[data-filter]').forEach(el => {
|
||||
el.onchange = filterOnChange;
|
||||
if (el.closest('.hidden')) {
|
||||
el.checked = false;
|
||||
}
|
||||
});
|
||||
|
||||
$('#reset-filters').onclick = event => {
|
||||
|
@ -150,7 +147,7 @@ function filterOnChange({target: el, forceRefilter}) {
|
|||
}
|
||||
el.lastValue = value;
|
||||
}
|
||||
const enabledFilters = $$('#header [data-filter]').filter(el => getValue(el));
|
||||
const enabledFilters = $$('#filters [data-filter]').filter(el => getValue(el));
|
||||
const buildFilter = hide =>
|
||||
(hide ? '' : '.entry.hidden') +
|
||||
[...enabledFilters.map(el =>
|
||||
|
@ -262,9 +259,9 @@ function reapplyFilter(container = installed, alreadySearched) {
|
|||
|
||||
function showFiltersStats() {
|
||||
const active = filtersSelector.hide !== '';
|
||||
$('#filters summary').classList.toggle('active', active);
|
||||
$('.filter-stats-wrapper').classList.toggle('active', active);
|
||||
$('#reset-filters').disabled = !active;
|
||||
const numTotal = installed.children.length;
|
||||
const numTotal = installed.children.length - 1; // Don't include the header
|
||||
const numHidden = installed.getElementsByClassName('entry hidden').length;
|
||||
const numShown = numTotal - numHidden;
|
||||
if (filtersSelector.numShown !== numShown ||
|
||||
|
|
|
@ -82,6 +82,7 @@ onDOMready().then(() => {
|
|||
// focus search field on "/" key
|
||||
if (key === '/' || !key && k === 191 && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
$('#manage-bulk-actions').classList.remove('hidden');
|
||||
$('#search').focus();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -74,12 +74,17 @@ function initGlobalEvents() {
|
|||
|
||||
document.addEventListener('keydown', event => {
|
||||
if (event.which === 27) {
|
||||
// close all open applies-to details
|
||||
// close all open "applies-to" details
|
||||
$$('.applies-to-extra[open]').forEach(el => {
|
||||
el.removeAttribute('open');
|
||||
});
|
||||
// Close bulk actions
|
||||
$('#manage-bulk-actions').classList.add('hidden');
|
||||
} else if (event.which === 32 && event.target.classList.contains('checkmate')) {
|
||||
// pressing space toggles the containing checkbox
|
||||
$('input[type="checkbox"]', event.target).click();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$$('[data-toggle-on-click]').forEach(el => {
|
||||
// dataset on SVG doesn't work in Chrome 49-??, works in 57+
|
||||
|
@ -123,7 +128,8 @@ Object.assign(handleEvent, {
|
|||
'.check-update': 'check',
|
||||
'.update': 'update',
|
||||
'.entry-delete': 'delete',
|
||||
'.entry-configure-usercss': 'config'
|
||||
'.entry-configure-usercss': 'config',
|
||||
'#toggle-actions': 'toggleBulkActions'
|
||||
},
|
||||
|
||||
entryClicked(event) {
|
||||
|
@ -182,6 +188,10 @@ Object.assign(handleEvent, {
|
|||
API.toggleStyle(entry.styleId, this.matches('.enable') || this.checked);
|
||||
},
|
||||
|
||||
toggleBulkActions() {
|
||||
$('#manage-bulk-actions').classList.toggle('hidden');
|
||||
},
|
||||
|
||||
check(event, entry) {
|
||||
event.preventDefault();
|
||||
checkUpdate(entry, {single: true});
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
--icon-size: 20px;
|
||||
--narrow-column: 60px;
|
||||
--header-height: 40px;
|
||||
--bulk-action-height: 60px;
|
||||
--entry-header-height: 25px;
|
||||
--onoffswitch-width: 60px;
|
||||
}
|
||||
|
@ -121,12 +122,7 @@ a:hover {
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.entry-header {
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
#installed .entry:nth-child(2) {
|
||||
#installed {
|
||||
margin-top: calc(var(--header-height) + var(--entry-header-height));
|
||||
}
|
||||
|
||||
|
@ -134,6 +130,11 @@ a:hover {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.entry-header {
|
||||
margin: 0;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.entry {
|
||||
margin: 0;
|
||||
padding: 4px 8px;
|
||||
|
@ -284,23 +285,18 @@ a svg, .svg-icon.sort {
|
|||
}
|
||||
|
||||
/* Checkbox */
|
||||
input.entry-filter:checked + svg.checkbox,
|
||||
input.entry-state-toggle:checked + svg.checkbox {
|
||||
.checkmate input:checked + svg.checkbox {
|
||||
background: #66bb6a;
|
||||
border-radius: var(--icon-size);
|
||||
}
|
||||
|
||||
input[type="checkbox"].entry-filter,
|
||||
input[type="checkbox"].entry-state-toggle,
|
||||
input.entry-filter:checked + svg.checkbox .circle,
|
||||
input.entry-state-toggle:checked + svg.checkbox .circle,
|
||||
input.entry-filter:not(:checked) + svg .checkmark,
|
||||
input.entry-state-toggle:not(:checked) + svg .checkmark {
|
||||
.checkmate input[type="checkbox"],
|
||||
.checkmate input:checked + svg.checkbox .circle,
|
||||
.checkmate input:not(:checked) + svg .checkmark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input.entry-filter:checked + svg.checkbox .checkmark,
|
||||
input.entry-state-toggle:checked + svg.checkbox .checkmark {
|
||||
.checkmate input:checked + svg.checkbox .checkmark {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
|
@ -310,6 +306,7 @@ input.entry-state-toggle:checked + svg.checkbox .checkmark {
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Update icons */
|
||||
.can-update .update,
|
||||
.no-update:not(.update-problem):not(.update-done) .up-to-date,
|
||||
.no-update.update-problem .check-update,
|
||||
|
@ -508,6 +505,29 @@ details.applies-to-extra[open] {
|
|||
display: block;
|
||||
}
|
||||
|
||||
/* bulk actions */
|
||||
#manage-bulk-actions {
|
||||
background: #ddd;
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
top: var(--header-height);
|
||||
left: 0;
|
||||
height: var(--bulk-action-height);
|
||||
padding: 4px 16px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#manage-bulk-actions:not(.hidden) + #installed {
|
||||
margin-top: calc(var(--header-height) + var(--bulk-action-height) + var(--entry-header-height));
|
||||
}
|
||||
#manage-bulk-actions:not(.hidden) + #installed .entry-header {
|
||||
top: calc(var(--header-height) + var(--bulk-action-height));
|
||||
}
|
||||
|
||||
.manage-row {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
/* highlight updated/added styles */
|
||||
.highlight {
|
||||
animation: highlight 10s cubic-bezier(0,.82,.47,.98);
|
||||
|
@ -529,6 +549,61 @@ details.applies-to-extra[open] {
|
|||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
#filters > label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
left: -9px;
|
||||
padding: 2px 0 2px 24px;
|
||||
}
|
||||
|
||||
#filters > label input[type="checkbox"]:not(.slider),
|
||||
#filters > label input[type="checkbox"]:not(.slider):checked + .svg-icon.checked {
|
||||
top: 2px;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
#filters > label:hover {
|
||||
background-color: hsla(0, 0%, 50%, .4);
|
||||
}
|
||||
|
||||
#filters {
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.active #filters-stats {
|
||||
background-color: darkcyan;
|
||||
border-color: darkcyan;
|
||||
color: white;
|
||||
font-size: 0.7rem;
|
||||
font-weight: normal;
|
||||
padding: 2px 5px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-stats-wrapper {
|
||||
margin-left: .2rem;
|
||||
}
|
||||
|
||||
#reset-filters svg {
|
||||
fill: hsla(180, 50%, 27%, .5);
|
||||
width: 24px; /* widen the click area a bit */
|
||||
height: 20px;
|
||||
padding: 2px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#reset-filters:hover svg {
|
||||
fill: hsla(180, 50%, 27%, 1);
|
||||
}
|
||||
|
||||
.filter-stats-wrapper:not(.active) #reset-filters,
|
||||
.filter-stats-wrapper:not(.active) #filters-stats {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* drag-n-drop on import button */
|
||||
.dropzone:after {
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
|
|
Loading…
Reference in New Issue
Block a user