Switch to filter text/buttons
This commit is contained in:
parent
2fd08cb041
commit
5047ceb1fa
|
@ -1253,8 +1253,8 @@
|
|||
"description": "Text for button to apply the selected action"
|
||||
},
|
||||
"bulkActionsTooltip": {
|
||||
"message": "Click to open the filter, search and bulk actions panel",
|
||||
"description": "Text for button to apply the selected action"
|
||||
"message": "Bulk actions can be applied to selected styles in this column",
|
||||
"description": "Select style for bulk action header tooltip"
|
||||
},
|
||||
"bulkActionsError": {
|
||||
"message": "Choose at least one style",
|
||||
|
|
|
@ -4,13 +4,108 @@
|
|||
(() => {
|
||||
// toLocaleLowerCase cache, autocleared after 1 minute
|
||||
const cache = new Map();
|
||||
// top-level style properties to be searched
|
||||
const PARTS = {
|
||||
name: searchText,
|
||||
url: searchText,
|
||||
sourceCode: searchText,
|
||||
sections: searchSections,
|
||||
};
|
||||
|
||||
// Creates an array of intermediate words (2 letter minimum)
|
||||
// 'usercss' => ["us", "use", "user", "userc", "usercs", "usercss"]
|
||||
// this makes it so the user can type partial queries and not have the search
|
||||
// constantly switching between using & ignoring the filter
|
||||
const createPartials = id => id.split('').reduce((acc, _, index) => {
|
||||
if (index > 0) {
|
||||
acc.push(id.substring(0, index + 1));
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const searchWithin = [{
|
||||
id: 'code',
|
||||
labels: createPartials('code'),
|
||||
get: style => style.sections.map(section => section.code).join(' ')
|
||||
}, {
|
||||
id: 'usercss',
|
||||
labels: [...createPartials('usercss'), ...createPartials('meta')],
|
||||
get: style => JSON.stringify(style.usercssData || {})
|
||||
// remove JSON structure; restore urls
|
||||
.replace(/[[\]{},":]/g, ' ').replace(/\s\/\//g, '://')
|
||||
}, {
|
||||
id: 'name', // default
|
||||
labels: createPartials('name'),
|
||||
get: style => style.name
|
||||
}];
|
||||
|
||||
const styleProps = [{
|
||||
id: 'enabled',
|
||||
labels: ['on', ...createPartials('enabled')],
|
||||
check: style => style.enabled
|
||||
}, {
|
||||
id: 'disabled',
|
||||
labels: ['off', ...createPartials('disabled')],
|
||||
check: style => !style.enabled
|
||||
}, {
|
||||
id: 'local',
|
||||
labels: createPartials('local'),
|
||||
check: style => !style.updateUrl
|
||||
}, {
|
||||
id: 'external',
|
||||
labels: createPartials('external'),
|
||||
check: style => style.updateUrl
|
||||
}, {
|
||||
id: 'usercss',
|
||||
labels: createPartials('usercss'),
|
||||
check: style => style.usercssData
|
||||
}, {
|
||||
id: 'non usercss',
|
||||
labels: ['original', ...createPartials('nonusercss')],
|
||||
check: style => !style.usercssData
|
||||
}];
|
||||
|
||||
const matchers = [{
|
||||
id: 'url',
|
||||
test: query => /url:\w+/i.test(query),
|
||||
matches: query => {
|
||||
const matchUrl = query.match(/url:([/.-_\w]+)/);
|
||||
const result = matchUrl && matchUrl[1]
|
||||
? styleManager.getStylesByUrl(matchUrl[1])
|
||||
.then(result => result.map(r => r.data.id))
|
||||
: [];
|
||||
return {result};
|
||||
},
|
||||
}, {
|
||||
id: 'regex',
|
||||
test: query => {
|
||||
const x = query.includes('/') && !query.includes('//') &&
|
||||
/^\/(.+?)\/([gimsuy]*)$/.test(query);
|
||||
// console.log('regex match?', query, x);
|
||||
return x;
|
||||
},
|
||||
matches: () => ({regex: tryRegExp(RegExp.$1, RegExp.$2)})
|
||||
}, {
|
||||
id: 'props',
|
||||
test: query => /is:/.test(query),
|
||||
matches: query => {
|
||||
const label = /is:(\w+)/g.exec(query);
|
||||
return label && label[1]
|
||||
? {prop: styleProps.find(p => p.labels.includes(label[1]))}
|
||||
: {};
|
||||
}
|
||||
}, {
|
||||
id: 'within',
|
||||
test: query => /in:/.test(query),
|
||||
matches: query => {
|
||||
const label = /in:(\w+)/g.exec(query);
|
||||
return label && label[1]
|
||||
? {within: searchWithin.find(s => s.labels.includes(label[1]))}
|
||||
: {};
|
||||
}
|
||||
}, {
|
||||
id: 'default',
|
||||
test: () => true,
|
||||
matches: query => {
|
||||
const word = query.startsWith('"') && query.endsWith('"')
|
||||
? query.slice(1, -1)
|
||||
: query;
|
||||
return {word: word || query};
|
||||
}
|
||||
}];
|
||||
|
||||
/**
|
||||
* @param params
|
||||
|
@ -19,77 +114,94 @@
|
|||
* @returns {number[]} - array of matched styles ids
|
||||
*/
|
||||
API_METHODS.searchDB = ({query, ids}) => {
|
||||
let rx, words, icase, matchUrl;
|
||||
query = query.trim();
|
||||
const parts = query.trim().split(/(".*?")|\s+/).filter(Boolean);
|
||||
|
||||
if (/^url:/i.test(query)) {
|
||||
matchUrl = query.slice(query.indexOf(':') + 1).trim();
|
||||
if (matchUrl) {
|
||||
return styleManager.getStylesByUrl(matchUrl)
|
||||
.then(results => results.map(r => r.data.id));
|
||||
const searchFilters = {
|
||||
words: [],
|
||||
regex: null, // only last regex expression is used
|
||||
results: [],
|
||||
props: [],
|
||||
within: [],
|
||||
};
|
||||
|
||||
const searchText = (text, searchFilters) => {
|
||||
if (searchFilters.regex) return searchFilters.regex.test(text);
|
||||
for (let pass = 1; pass <= (searchFilters.icase ? 2 : 1); pass++) {
|
||||
if (searchFilters.words.every(w => text.includes(w))) return true;
|
||||
text = lower(text);
|
||||
}
|
||||
};
|
||||
|
||||
const searchProps = (style, searchFilters) => {
|
||||
const x = searchFilters.props.every(prop => {
|
||||
const y = prop.check(style)
|
||||
// if (y) console.log('found prop', prop.id, style.id)
|
||||
return y;
|
||||
});
|
||||
// if (x) console.log('found prop', style.id)
|
||||
return x;
|
||||
};
|
||||
|
||||
parts.forEach(part => {
|
||||
matchers.some(matcher => {
|
||||
if (matcher.test(part)) {
|
||||
const {result, regex, word, prop, within} = matcher.matches(part || '');
|
||||
if (result) searchFilters.results.push(result);
|
||||
if (regex) searchFilters.regex = regex; // limited to a single regexp
|
||||
if (word) searchFilters.words.push(word);
|
||||
if (prop) searchFilters.props.push(prop);
|
||||
if (within) searchFilters.within.push(within);
|
||||
return true;
|
||||
}
|
||||
if (query.startsWith('/') && /^\/(.+?)\/([gimsuy]*)$/.test(query)) {
|
||||
rx = tryRegExp(RegExp.$1, RegExp.$2);
|
||||
});
|
||||
});
|
||||
if (!searchFilters.within.length) {
|
||||
searchFilters.within.push(...searchWithin.slice(-1));
|
||||
}
|
||||
if (!rx) {
|
||||
words = query
|
||||
.split(/(".*?")|\s+/)
|
||||
.filter(Boolean)
|
||||
.map(w => w.startsWith('"') && w.endsWith('"')
|
||||
? w.slice(1, -1)
|
||||
: w)
|
||||
.filter(w => w.length > 1);
|
||||
words = words.length ? words : [query];
|
||||
icase = words.some(w => w === lower(w));
|
||||
|
||||
// console.log('matchers', searchFilters);
|
||||
// url matches
|
||||
if (searchFilters.results.length) {
|
||||
return searchFilters.results;
|
||||
}
|
||||
searchFilters.icase = searchFilters.words.some(w => w === lower(w));
|
||||
query = parts.join(' ').trim();
|
||||
|
||||
return styleManager.getAllStyles().then(styles => {
|
||||
if (ids) {
|
||||
const idSet = new Set(ids);
|
||||
styles = styles.filter(s => idSet.has(s.id));
|
||||
}
|
||||
|
||||
const results = [];
|
||||
const propResults = [];
|
||||
const hasProps = searchFilters.props.length > 0;
|
||||
const noWords = searchFilters.words.length === 0;
|
||||
for (const style of styles) {
|
||||
const id = style.id;
|
||||
if (!query || words && !words.length) {
|
||||
if (noWords) {
|
||||
// no query or only filters are matching -> show all styles
|
||||
results.push(id);
|
||||
continue;
|
||||
}
|
||||
for (const part in PARTS) {
|
||||
const text = style[part];
|
||||
if (text && PARTS[part](text, rx, words, icase)) {
|
||||
} else {
|
||||
const text = searchFilters.within.map(within => within.get(style)).join(' ');
|
||||
if (searchText(text, searchFilters)) {
|
||||
results.push(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasProps && searchProps(style, searchFilters) && results.includes(id)) {
|
||||
propResults.push(id);
|
||||
}
|
||||
}
|
||||
// results AND propResults
|
||||
const finalResults = hasProps
|
||||
? propResults.filter(id => results.includes(id))
|
||||
: results;
|
||||
if (cache.size) debounce(clearCache, 60e3);
|
||||
return results;
|
||||
// console.log('final', finalResults)
|
||||
return finalResults;
|
||||
});
|
||||
};
|
||||
|
||||
function searchText(text, rx, words, icase) {
|
||||
if (rx) return rx.test(text);
|
||||
for (let pass = 1; pass <= (icase ? 2 : 1); pass++) {
|
||||
if (words.every(w => text.includes(w))) return true;
|
||||
text = lower(text);
|
||||
}
|
||||
}
|
||||
|
||||
function searchSections(sections, rx, words, icase) {
|
||||
for (const section of sections) {
|
||||
for (const prop in section) {
|
||||
const value = section[prop];
|
||||
if (typeof value === 'string') {
|
||||
if (searchText(value, rx, words, icase)) return true;
|
||||
} else if (Array.isArray(value)) {
|
||||
if (value.some(str => searchText(str, rx, words, icase))) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lower(text) {
|
||||
let result = cache.get(text);
|
||||
if (result) return result;
|
||||
|
|
321
manage.html
321
manage.html
|
@ -221,10 +221,7 @@
|
|||
<span class="filter-stats-wrapper">
|
||||
<span id="filters-stats"></span>
|
||||
<a id="reset-filters" class="tt-e" href="#" tabindex="0" i18n-data-title="genericResetLabel">
|
||||
<svg class="svg-icon" viewBox="0 0 20 20">
|
||||
<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>
|
||||
<svg class="svg-icon" viewBox="0 0 20 20"><use xlink:href="#svg-icon-x"/></svg>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -256,12 +253,6 @@
|
|||
|
||||
<span class="spacer"></span>
|
||||
|
||||
<a href="#" id="update-all" class="tt-w" i18n-data-title="checkAllUpdates">
|
||||
<svg class="svg-icon" viewBox="0 0 24 24">
|
||||
<path d="M11 8v5l4.25 2.52.77-1.28-3.52-2.09V8zm10 2V3l-2.64 2.64A9 9 0 1 0 21 12h-2a7 7 0 1 1-2.05-4.95L14 10h7z"/>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<div class="manage-backups tt-w" i18n-data-title="backupImport">
|
||||
<svg class="svg-icon" viewBox="0 0 24 24">
|
||||
<path d="M20.54 5.23l-1.39-1.68A1.45 1.45 0 0 0 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14a2 2 0 0 0 2-2V6.5c0-.48-.17-.93-.46-1.27zM6.24 5h11.52l.81.97H5.44l.8-.97zM5 19V8h14v11H5zm8.45-9h-2.9v3H8l4 4 4-4h-2.55z"/>
|
||||
|
@ -329,98 +320,7 @@
|
|||
|
||||
</h1>
|
||||
|
||||
<div id="tools-wrapper" class="hidden">
|
||||
|
||||
<div 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>
|
||||
|
||||
<span id="filters-wrapper">
|
||||
<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 10 10">
|
||||
<path class="filled-circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86z"/>
|
||||
<path class="circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86zm0 7.5a3.36 3.36 0 1 1 0-6.72 3.36 3.36 0 0 1 0 6.72z"/>
|
||||
<path class="checkmark" d="M6.86 3.21L4.14 5.93 3.07 4.86l-.57.57 1.64 1.71L7.5 3.8l-.64-.58z"/>
|
||||
</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 10 10">
|
||||
<path class="filled-circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86z"/>
|
||||
<path class="circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86zm0 7.5a3.36 3.36 0 1 1 0-6.72 3.36 3.36 0 0 1 0 6.72z"/>
|
||||
<path class="checkmark" d="M6.86 3.21L4.14 5.93 3.07 4.86l-.57.57 1.64 1.71L7.5 3.8l-.64-.58z"/>
|
||||
</svg>
|
||||
</label>
|
||||
<span class="select-resizer">
|
||||
<select id="manage.onlyLocal.invert" class="tt-s" i18n-data-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 10 10">
|
||||
<path class="filled-circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86z"/>
|
||||
<path class="circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86zm0 7.5a3.36 3.36 0 1 1 0-6.72 3.36 3.36 0 0 1 0 6.72z"/>
|
||||
<path class="checkmark" d="M6.86 3.21L4.14 5.93 3.07 4.86l-.57.57 1.64 1.71L7.5 3.8l-.64-.58z"/>
|
||||
</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>
|
||||
|
||||
<span class="filter-selection">
|
||||
<label id="only-updates" class="hidden 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 10 10">
|
||||
<path class="filled-circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86z"/>
|
||||
<path class="circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86zm0 7.5a3.36 3.36 0 1 1 0-6.72 3.36 3.36 0 0 1 0 6.72z"/>
|
||||
<path class="checkmark" d="M6.86 3.21L4.14 5.93 3.07 4.86l-.57.57 1.64 1.71L7.5 3.8l-.64-.58z"/>
|
||||
</svg>
|
||||
<span i18n-text="manageOnlyUpdates"></span>
|
||||
</label>
|
||||
</span>
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<div id="tools-wrapper">
|
||||
|
||||
<div id="bulk-actions" class="manage-row">
|
||||
<label class="checkmate toggle-all" tabindex="0">
|
||||
|
@ -450,6 +350,13 @@
|
|||
<button id="bulk-actions-apply" i18n-text="bulkActionsApply" class="tt-e" disabled>
|
||||
<span id="update-progress"></span>
|
||||
</button>
|
||||
|
||||
<button href="#" id="update-all" class="tt-w" i18n-data-title="checkAllUpdates" type="button">
|
||||
<svg class="svg-icon" viewBox="0 0 24 24">
|
||||
<path d="M11 8v5l4.25 2.52.77-1.28-3.52-2.09V8zm10 2V3l-2.64 2.64A9 9 0 1 0 21 12h-2a7 7 0 1 1-2.05-4.95L14 10h7z"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<span id="bulk-info">
|
||||
<!-- Bulk update -->
|
||||
<span data-bulk="update">
|
||||
|
@ -485,13 +392,96 @@
|
|||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="manage-row">
|
||||
<div id="search-wrapper">
|
||||
<input id="search" type="search" i18n-placeholder="searchStyles" spellcheck="false"
|
||||
data-filter=":not(.not-matching)"
|
||||
data-filter-hide=".not-matching">
|
||||
</div>
|
||||
<div id="filters-wrapper">
|
||||
<button
|
||||
class="reset-filters search-filter tt-w"
|
||||
type="button"
|
||||
i18n-data-title="genericResetLabel"
|
||||
data-filter=".entry"
|
||||
data-filter-hide=".disabled"
|
||||
>
|
||||
<svg class="svg-icon"><use xlink:href="#svg-icon-x"/></svg>
|
||||
</button>
|
||||
<span class="button-group">
|
||||
<label class="search-filter tt-w" i18n-data-title="manageOnlyEnabled">
|
||||
<input
|
||||
id="manage.onlyEnabled"
|
||||
name="enabled"
|
||||
type="radio"
|
||||
data-filter=".enabled"
|
||||
data-filter-hide=".disabled"
|
||||
/>
|
||||
<svg class="svg-icon checkbox-enabled" viewBox="0 0 10 10">
|
||||
<path d="M6.86 3.21L4.14 5.93 3.07 4.86l-.57.57 1.64 1.71L7.5 3.8l-.64-.58z"/>
|
||||
</svg>
|
||||
</label>
|
||||
<label class="search-filter tt-w" i18n-data-title="manageOnlyDisabled">
|
||||
<input id="manage.onlyEnabled.invert" name="enabled" type="radio" />
|
||||
<svg class="svg-icon checkbox"viewBox="0 0 10 10">
|
||||
<path class="circle" d="M5 .86a4.14 4.14 0 0 0 0 8.28A4.14 4.14 0 1 0 5 .86zm0 7.5a3.36 3.36 0 1 1 0-6.72 3.36 3.36 0 0 1 0 6.72z"/>
|
||||
</svg>
|
||||
</label>
|
||||
</span>
|
||||
<span class="button-group">
|
||||
<label class="search-filter tt-w" i18n-data-title="manageOnlyUsercss">
|
||||
<input
|
||||
id="manage.onlyUsercss"
|
||||
name="usercss"
|
||||
type="radio"
|
||||
data-filter=".usercss"
|
||||
data-filter-hide=":not(.usercss)"
|
||||
/>
|
||||
<span>usercss</span> <!-- TODO: localize -->
|
||||
</label>
|
||||
<label class="search-filter tt-w" i18n-data-title="manageOnlyNonUsercss">
|
||||
<input type="radio" id="manage.onlyUsercss.invert" name="usercss" />
|
||||
<span>non-usercss</span> <!-- TODO: localize -->
|
||||
</label>
|
||||
</span>
|
||||
<span class="button-group">
|
||||
<label class="search-filter tt-w" i18n-data-title="manageOnlyLocal">
|
||||
<input
|
||||
id="manage.onlyLocal"
|
||||
name="local"
|
||||
type="radio"
|
||||
data-filter=":not(.updatable):not(.update-done)"
|
||||
data-filter-hide=".updatable, .update-done"
|
||||
/>
|
||||
<span>local</span> <!-- TODO: localize -->
|
||||
</label>
|
||||
<label class="search-filter tt-w" i18n-data-title="manageOnlyExternal">
|
||||
<input id="manage.onlyLocal.invert" name="local" type="radio" />
|
||||
<span>external</span> <!-- TODO: localize -->
|
||||
</label>
|
||||
</span>
|
||||
<label class="search-filter hidden">
|
||||
<input
|
||||
id="only-updates"
|
||||
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)"
|
||||
/>
|
||||
<span i18n-text="manageOnlyUpdates"></span>
|
||||
</label>
|
||||
<a href="#" id="search-help">
|
||||
<svg class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="installed" class="manage-col-entries">
|
||||
<header class="entry-header">
|
||||
<div class="entry-col header-filter center-text tt-se" i18n-data-title="bulkActionsTooltip">
|
||||
<span></span>
|
||||
<svg class="svg-icon" width="20" height="20" viewBox="0 0 14 14">
|
||||
<svg class="svg-icon no-pointer" 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>
|
||||
</div>
|
||||
|
@ -563,125 +553,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div id="manage-settings">
|
||||
<div class="settings-column">
|
||||
|
||||
<div id="style-actions">
|
||||
|
||||
|
||||
<div id="add-style-wrapper">
|
||||
<a href="edit.html">
|
||||
<button id="add-style-label" i18n-text="addStyleLabel" tabindex="-1"></button>
|
||||
</a>
|
||||
<label id="add-style-as-usercss-wrapper">
|
||||
<input type="checkbox" id="newStyleAsUsercss">
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
<span i18n-text="manageNewStyleAsUsercss" i18n-title="optionsAdvancedNewStyleAsUsercss"></span>
|
||||
<a id="usercss-wiki"
|
||||
href="https://github.com/openstyles/stylus/wiki/Usercss"
|
||||
i18n-title="externalUsercssDocument"
|
||||
tabindex="0">
|
||||
<svg class="svg-icon" viewBox="0 0 20 20">
|
||||
<path d="M4,4h5v2H6v8h8v-3h2v5H4V4z M11,3h6v6l-2-2l-4,4L9,9l4-4L11,3z"/>
|
||||
</svg>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-column">
|
||||
<details id="options" data-pref="manage.options.expanded">
|
||||
|
||||
<summary><h2 id="options-heading" i18n-text="optionsHeading"></h2></summary>
|
||||
|
||||
<label>
|
||||
<input id="manage.newUI" type="checkbox">
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
<span i18n-text="manageNewUI"></span>
|
||||
</label>
|
||||
|
||||
<div id="newUIoptions">
|
||||
<div>
|
||||
<label for="manage.newUI.favicons" i18n-text="manageFavicons">
|
||||
<input id="manage.newUI.favicons" type="checkbox">
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
<a href="#" data-toggle-on-click="#faviconsHelp" tabindex="0">
|
||||
<svg class="svg-icon select-arrow">
|
||||
<title i18n-text="optionsSubheading"></title>
|
||||
<use xlink:href="#svg-icon-select-arrow"/>
|
||||
</svg>
|
||||
</a>
|
||||
</label>
|
||||
<div id="faviconsHelp" class="hidden" i18n-text="manageFaviconsHelp">
|
||||
<div>
|
||||
<label for="manage.newUI.faviconsGray" i18n-text="manageFaviconsGray">
|
||||
<input id="manage.newUI.faviconsGray" type="checkbox">
|
||||
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label><input id="manage.newUI.targets" type="number" min="1" max="99"><span i18n-text="manageMaxTargets"></span></label>
|
||||
</div>
|
||||
|
||||
<div id="options-buttons">
|
||||
<button id="manage-options-button" i18n-text="openOptions"></button>
|
||||
<button id="manage-shortcuts-button" class="chromium-only"
|
||||
i18n-text="shortcuts"
|
||||
i18n-title="shortcutsNote"></button>
|
||||
<a id="find-editor-styles"
|
||||
href="https://userstyles.org/styles/browse/chrome-extension"
|
||||
i18n-title="editorStylesButton"
|
||||
target="_blank"><button i18n-text="cm_theme" tabindex="-1"></button></a>
|
||||
</div>
|
||||
|
||||
</details>
|
||||
|
||||
<details id="backup" data-pref="manage.backup.expanded">
|
||||
<summary><h2 id="backup-title" i18n-text="backupButtons"></h2></summary>
|
||||
<span id="backup-message" i18n-text="backupMessage"></span>
|
||||
<div id="backup-buttons">
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn">
|
||||
<span>Export</span>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</button>
|
||||
|
||||
<div class="dropdown-content">
|
||||
<a href="#" id="file-all-styles" i18n-text="bckpInstStyles"></a>
|
||||
<a id="sync-dropbox-export" i18n-text="syncDropboxStyles" i18n-title="syncDropboxDeprecated"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="dropbtn">
|
||||
<span>Import</span>
|
||||
<svg class="svg-icon select-arrow"><use xlink:href="#svg-icon-select-arrow"/></svg>
|
||||
</button>
|
||||
|
||||
<div class="dropdown-content">
|
||||
<a href="#" id="unfile-all-styles" i18n-text="retrieveBckp"></a>
|
||||
<a id="sync-dropbox-import" i18n-text="retrieveDropboxSync" i18n-title="syncDropboxDeprecated"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div id="manage-text">
|
||||
<span><a href="https://userstyles.org" target="_blank" i18n-text="linkGetStyles"></a></span>
|
||||
<span><a href="https://add0n.com/stylus.html#features" target="_blank" i18n-text="linkGetHelp"></a></span>
|
||||
<span><a href="https://github.com/openstyles/stylus/wiki" target="_blank" i18n-text="linkStylusWiki"></a></span>
|
||||
<span><a href="https://www.transifex.com/github-7/Stylus" target="_blank" i18n-text="linkTranslate"></a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="installed"></div> -->
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none !important;">
|
||||
<symbol id="svg-icon-checked" viewBox="0 0 1000 1000">
|
||||
<path fill-rule="evenodd" d="M983.2,184.3L853,69.8c-4-3.5-9.3-5.3-14.5-5c-5.3,0.4-10.3,2.8-13.8,
|
||||
|
@ -723,6 +594,10 @@
|
|||
1.13zM12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zm0 6c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/>
|
||||
</symbol>
|
||||
|
||||
<symbol id="svg-icon-x" viewBox="0 0 20 20">
|
||||
<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 "/>
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -104,7 +104,6 @@ const bulk = {
|
|||
if (installed.dataset.total) {
|
||||
// ignore filter checkboxes
|
||||
if (target.type === 'checkbox' && target.closest('.toggle-all, .entry-filter')) {
|
||||
handleEvent.toggleBulkActions({hidden: false});
|
||||
const bulk = $('#toggle-all-filters');
|
||||
const state = target.checked;
|
||||
const visibleEntries = $$('.entry-filter-toggle')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global installed messageBox sorter $ $$ $create t debounce prefs API router */
|
||||
/* global installed messageBox sorter $ $$ $create t debounce prefs API UI router resetUpdates */
|
||||
/* exported filterAndAppend */
|
||||
'use strict';
|
||||
|
||||
|
@ -37,8 +37,9 @@ HTMLSelectElement.prototype.adjustWidth = function () {
|
|||
};
|
||||
|
||||
function init() {
|
||||
$('#search').oninput = e => {
|
||||
router.updateSearch('search', e.target.value);
|
||||
$('#search').oninput = event => {
|
||||
router.updateSearch('search', event.target.value);
|
||||
UI.updateFilterLabels();
|
||||
};
|
||||
|
||||
$('#search-help').onclick = event => {
|
||||
|
@ -57,48 +58,13 @@ function init() {
|
|||
} else {
|
||||
return s;
|
||||
}
|
||||
})))),
|
||||
}))
|
||||
)
|
||||
),
|
||||
buttons: [t('confirmOK')],
|
||||
});
|
||||
};
|
||||
|
||||
$$('select[id$=".invert"]').forEach(el => {
|
||||
const slave = $('#' + el.id.replace('.invert', ''));
|
||||
const slaveData = slave.dataset;
|
||||
const valueMap = new Map([
|
||||
[false, slaveData.filter],
|
||||
[true, slaveData.filterHide],
|
||||
]);
|
||||
// enable slave control when user switches the value
|
||||
el.oninput = () => {
|
||||
if (!slave.checked) {
|
||||
// oninput occurs before onchange
|
||||
setTimeout(() => {
|
||||
if (!slave.checked) {
|
||||
slave.checked = true;
|
||||
slave.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
// swap slave control's filtering rules
|
||||
el.onchange = event => {
|
||||
const value = el.value === 'true';
|
||||
const filter = valueMap.get(value);
|
||||
if (slaveData.filter === filter) {
|
||||
return;
|
||||
}
|
||||
slaveData.filter = filter;
|
||||
slaveData.filterHide = valueMap.get(!value);
|
||||
debounce(filterOnChange, 0, event);
|
||||
// avoid triggering MutationObserver during page load
|
||||
if (document.readyState === 'complete') {
|
||||
el.adjustWidth();
|
||||
}
|
||||
};
|
||||
el.onchange({target: el});
|
||||
});
|
||||
|
||||
$$('[data-filter]').forEach(el => {
|
||||
el.onchange = filterOnChange;
|
||||
if (el.closest('.hidden')) {
|
||||
|
@ -108,9 +74,9 @@ function init() {
|
|||
|
||||
$('#reset-filters').onclick = event => {
|
||||
event.preventDefault();
|
||||
if (!filtersSelector.hide) {
|
||||
return;
|
||||
}
|
||||
// if (!filtersSelector.hide) {
|
||||
// return;
|
||||
// }
|
||||
for (const el of $$('#tools-wrapper [data-filter]')) {
|
||||
let value;
|
||||
if (el.type === 'checkbox' && el.checked) {
|
||||
|
@ -127,6 +93,8 @@ function init() {
|
|||
}
|
||||
filterOnChange({forceRefilter: true});
|
||||
router.updateSearch('search', '');
|
||||
resetUpdates();
|
||||
UI.updateFilterLabels();
|
||||
};
|
||||
|
||||
filterOnChange({forceRefilter: true});
|
||||
|
@ -134,7 +102,7 @@ function init() {
|
|||
|
||||
|
||||
function filterOnChange({target: el, forceRefilter}) {
|
||||
const getValue = el => (el.type === 'checkbox' ? el.checked : el.value.trim());
|
||||
const getValue = elm => (elm.type === 'search') ? elm.value.trim() : elm.checked;
|
||||
if (!forceRefilter) {
|
||||
const value = getValue(el);
|
||||
if (value === el.lastValue) {
|
||||
|
@ -155,6 +123,7 @@ function filterOnChange({target: el, forceRefilter}) {
|
|||
hide: buildFilter(true),
|
||||
unhide: buildFilter(false),
|
||||
});
|
||||
console.log('filter on change', filtersSelector, installed)
|
||||
if (installed) {
|
||||
reapplyFilter().then(sorter.updateStripes);
|
||||
}
|
||||
|
@ -283,6 +252,7 @@ function searchStyles({immediately, container} = {}) {
|
|||
el.lastValue = query;
|
||||
|
||||
const entries = container && container.children || container || installed.children;
|
||||
console.log('search?', query)
|
||||
return API.searchDB({
|
||||
query,
|
||||
ids: [...entries].map(el => el.styleId),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* global messageBox styleSectionsEqual API onDOMready
|
||||
tryJSONparse scrollElementIntoView $ $$ API $create t animateElement
|
||||
handleEvent
|
||||
styleJSONseemsValid */
|
||||
'use strict';
|
||||
|
||||
|
@ -46,8 +47,9 @@ onDOMready().then(() => {
|
|||
this.ondragend();
|
||||
if (event.dataTransfer.files.length) {
|
||||
event.preventDefault();
|
||||
if ($('#only-updates input').checked) {
|
||||
$('#only-updates input').click();
|
||||
const updates = $('#only-updates');
|
||||
if (updates.checked) {
|
||||
handleEvent.checkFilterSelectors(updates);
|
||||
}
|
||||
importFromFile({file: event.dataTransfer.files[0]});
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
global messageBox getStyleWithNoCode
|
||||
filterAndAppend showFiltersStats
|
||||
checkUpdate handleUpdateInstalled
|
||||
checkUpdate handleUpdateInstalled resetUpdates
|
||||
objectDiff
|
||||
configDialog
|
||||
sorter msg prefs API onDOMready $ $$ setupLivePrefs
|
||||
|
@ -80,8 +80,19 @@ function initGlobalEvents() {
|
|||
|
||||
$('#update-all').onclick = event => {
|
||||
event.preventDefault();
|
||||
handleEvent.toggleBulkActions({hidden: false});
|
||||
bulk.updateAll();
|
||||
};
|
||||
|
||||
$('#filters-wrapper').onclick = event => {
|
||||
event.preventDefault();
|
||||
handleEvent.toggleFilter(event.target);
|
||||
};
|
||||
|
||||
$('#search').onsearch = event => {
|
||||
if (event.target.value === '') {
|
||||
console.log('search empty')
|
||||
handleEvent.resetFilters();
|
||||
}
|
||||
}
|
||||
|
||||
$$('#header a[href^="http"]').forEach(a => (a.onclick = handleEvent.external));
|
||||
|
@ -95,28 +106,12 @@ function initGlobalEvents() {
|
|||
$$('.applies-to-extra[open]').forEach(el => {
|
||||
el.removeAttribute('open');
|
||||
});
|
||||
// Close bulk actions
|
||||
handleEvent.toggleBulkActions({hidden: true});
|
||||
} 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+
|
||||
const target = $(el.getAttribute('data-toggle-on-click'));
|
||||
el.onclick = event => {
|
||||
event.preventDefault();
|
||||
target.classList.toggle('hidden');
|
||||
if (target.classList.contains('hidden')) {
|
||||
el.removeAttribute('open');
|
||||
} else {
|
||||
el.setAttribute('open', '');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// triggered automatically by setupLivePrefs() below
|
||||
enforceInputRange($('#manage.newUI.targets'));
|
||||
|
||||
|
@ -146,7 +141,6 @@ Object.assign(handleEvent, {
|
|||
'.update': 'update',
|
||||
'.entry-delete': 'delete',
|
||||
'.entry-configure-usercss': 'config',
|
||||
'.header-filter': 'toggleBulkActions',
|
||||
'.sortable': 'updateSort',
|
||||
'#applies-to-config': 'appliesConfig',
|
||||
'.applies-to-extra-expander': 'toggleExtraAppliesTo'
|
||||
|
@ -215,12 +209,6 @@ Object.assign(handleEvent, {
|
|||
UI.addLabels(entry);
|
||||
},
|
||||
|
||||
toggleBulkActions({hidden}) {
|
||||
const tools = $('#tools-wrapper');
|
||||
tools.classList.toggle('hidden', hidden);
|
||||
$('.header-filter').classList.toggle('active', !tools.classList.contains('hidden'));
|
||||
},
|
||||
|
||||
toggleExtraAppliesTo(event, entry) {
|
||||
event.preventDefault();
|
||||
entry.classList.toggle('hide-extra');
|
||||
|
@ -230,6 +218,59 @@ Object.assign(handleEvent, {
|
|||
}
|
||||
},
|
||||
|
||||
resetFilters() {
|
||||
$('#reset-filters').click();
|
||||
// TODO: figure out why we need to press this twice
|
||||
$('#reset-filters').click();
|
||||
resetUpdates();
|
||||
},
|
||||
|
||||
toggleFilter(el) {
|
||||
if (el.classList.contains('reset-filters')) {
|
||||
return handleEvent.resetFilters();
|
||||
}
|
||||
|
||||
const target = (el.nodeName === 'LABEL') ? $('input', el) : el;
|
||||
const type = Object.values(UI.searchFilters).find(filter => filter.id === target.id);
|
||||
const filterQuery = type && type.query || '';
|
||||
const remove = type && type.invert ? UI.searchFilters[type.invert].query : '';
|
||||
const len = filterQuery.length + 1;
|
||||
const search = $('#search');
|
||||
|
||||
let {selectionStart, selectionEnd, value} = search;
|
||||
if (value.includes(filterQuery)) {
|
||||
value = ` ${value} `.replace(` ${filterQuery} `, ' ').trim();
|
||||
if (selectionEnd > value.length) {
|
||||
selectionStart -= len;
|
||||
selectionEnd -= len;
|
||||
}
|
||||
} else {
|
||||
if (selectionEnd === value.length) {
|
||||
selectionStart += len;
|
||||
selectionEnd += len;
|
||||
}
|
||||
value = (` ${value} ${filterQuery} `.replace(` ${remove} `, ' ')).trim();
|
||||
}
|
||||
search.value = value;
|
||||
search.selectionStart = selectionStart;
|
||||
search.selectionEnd = selectionEnd;
|
||||
search.focus();
|
||||
router.updateSearch('search', value);
|
||||
UI.updateFilterLabels();
|
||||
// updates or issues (special case)
|
||||
if (target.dataset.filterSelectors) {
|
||||
handleEvent.checkFilterSelectors(target);
|
||||
}
|
||||
},
|
||||
|
||||
checkFilterSelectors(target) {
|
||||
const selectors = target.dataset.filterSelectors;
|
||||
const checked = target.classList.contains('checked');
|
||||
$$('.entry').forEach(entry => {
|
||||
entry.classList.toggle('hidden', checked && !entry.matches(selectors));
|
||||
});
|
||||
},
|
||||
|
||||
check(event, entry) {
|
||||
event.preventDefault();
|
||||
checkUpdate(entry, {single: true});
|
||||
|
|
|
@ -40,6 +40,11 @@ const UI = {
|
|||
content: "${t('filteredStylesAllHidden')}";
|
||||
}
|
||||
`));
|
||||
// remove update filter on init
|
||||
const search = $('#search');
|
||||
search.value = search.value.replace(UI.searchFilters.updatable.query, '');
|
||||
// update filter labels to match location.search
|
||||
UI.updateFilterLabels();
|
||||
},
|
||||
|
||||
showStyles: (styles = [], matchUrlIds) => {
|
||||
|
@ -178,6 +183,24 @@ const UI = {
|
|||
: '';
|
||||
},
|
||||
|
||||
updateFilterLabels: () => {
|
||||
const filterLabels = $$('#filters-wrapper .search-filter input');
|
||||
filterLabels.forEach(cb => {
|
||||
cb.checked = false;
|
||||
cb.parentElement.classList.remove('checked');
|
||||
});
|
||||
const filters = Object.values(UI.searchFilters);
|
||||
$('#search').value.split(' ').forEach(part => {
|
||||
const filter = filters.find(entry => entry.query === part);
|
||||
if (filter) {
|
||||
const button = filterLabels.filter(btn => btn.id === filter.id);
|
||||
if (button.length) {
|
||||
button[0].checked = true;
|
||||
button[0].parentElement.classList.add('checked');
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
createStyleTargetsElement: ({entry, style}) => {
|
||||
const parts = UI._parts;
|
||||
|
@ -226,6 +249,48 @@ const UI = {
|
|||
entry.classList.toggle('global', !numTargets);
|
||||
},
|
||||
|
||||
// This order matters
|
||||
searchFilters: {
|
||||
enabled: {
|
||||
id: 'manage.onlyEnabled',
|
||||
query: 'is:enabled',
|
||||
invert: 'disabled'
|
||||
},
|
||||
disabled: {
|
||||
id: 'manage.onlyEnabled.invert',
|
||||
query: 'is:disabled',
|
||||
invert: 'enabled'
|
||||
},
|
||||
usercss: {
|
||||
id: 'manage.onlyUsercss',
|
||||
query: 'is:usercss',
|
||||
invert: 'original'
|
||||
},
|
||||
original: {
|
||||
id: 'manage.onlyUsercss.invert',
|
||||
query: 'is:nonusercss',
|
||||
invert: 'usercss'
|
||||
},
|
||||
local: {
|
||||
id: 'manage.onlyLocal',
|
||||
query: 'is:local',
|
||||
invert: 'external'
|
||||
},
|
||||
external: {
|
||||
id: 'manage.onlyLocal.invert',
|
||||
query: 'is:external',
|
||||
invert: 'local'
|
||||
},
|
||||
// only checkbox; all others are radio buttons
|
||||
updatable: {
|
||||
id: 'only-updates',
|
||||
query: '', // 'has:updates',
|
||||
},
|
||||
reset: {
|
||||
id: 'reset-filters',
|
||||
query: ''
|
||||
}
|
||||
},
|
||||
|
||||
getFaviconImgSrc: (container = installed) => {
|
||||
if (!UI.favicons) return;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
--favicon-size: 16px;
|
||||
--narrow-column: 60px;
|
||||
--header-height: 40px;
|
||||
--toolbar-height: 60px;
|
||||
--toolbar-height: 40px;
|
||||
--entry-height: 30px;
|
||||
--onoffswitch-width: 60px;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
|||
--header-icon-hover-color: #2afefe;
|
||||
|
||||
--tools-bg-color: #ccc;
|
||||
--tools-bg-hover: #eee;
|
||||
|
||||
--entry-header-bg-color: #ddd;
|
||||
--entry-header-text-color: #111;
|
||||
|
@ -71,13 +72,17 @@ a:hover {
|
|||
color: var(--entry-text-hover);
|
||||
}
|
||||
|
||||
body a[disabled],
|
||||
body button[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
cursor: pointer;
|
||||
width: var(--entry-icon-size);
|
||||
height: var(--entry-icon-size);
|
||||
vertical-align: middle;
|
||||
|
@ -85,6 +90,10 @@ a:hover {
|
|||
fill: var(--entry-icon-color);
|
||||
}
|
||||
|
||||
.svg-icon:not(.no-pointer) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#main-header .svg-icon {
|
||||
width: var(--header-icon-size);
|
||||
height: var(--header-icon-size);
|
||||
|
@ -122,6 +131,7 @@ a:hover {
|
|||
|
||||
#bulk-actions {
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#bulk-info > span,
|
||||
|
@ -147,9 +157,9 @@ a:hover {
|
|||
100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); }
|
||||
}
|
||||
|
||||
.entry-header a:hover .svg-icon,
|
||||
.entry a:hover .svg-icon,
|
||||
.svg-icon:hover {
|
||||
.entry-header a:hover .svg-icon:not(.no-pointer),
|
||||
.entry a:hover .svg-icon:not(.no-pointer),
|
||||
.svg-icon:not(.no-pointer):hover {
|
||||
fill: var(--entry-icon-hover-color);
|
||||
}
|
||||
|
||||
|
@ -340,14 +350,6 @@ body.all-styles-hidden-by-filters #installed:after {
|
|||
content: '▾';
|
||||
}
|
||||
|
||||
.header-filter span:before {
|
||||
content: '►';
|
||||
color: var(--entry-icon-color);
|
||||
position: relative;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.header-filter:hover span:before,
|
||||
.header-filter:hover .svg-icon,
|
||||
.header-filter.active svg {
|
||||
transition: all .5s;
|
||||
|
@ -355,11 +357,6 @@ body.all-styles-hidden-by-filters #installed:after {
|
|||
fill: var(--entry-text-hover);
|
||||
}
|
||||
|
||||
.header-filter.active span:before {
|
||||
content: '▲';
|
||||
color: var(--entry-text-hover);
|
||||
}
|
||||
|
||||
.targets {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
@ -473,7 +470,8 @@ a svg, .svg-icon.sort {
|
|||
}
|
||||
|
||||
/* Checkbox */
|
||||
.checkmate input:checked + svg.checkbox .filled-circle {
|
||||
.checkmate input:checked + svg.checkbox .filled-circle,
|
||||
svg.checkbox-enabled .filled-circle {
|
||||
fill: var(--checkbox-bg-color);
|
||||
display: block;
|
||||
}
|
||||
|
@ -486,7 +484,8 @@ a svg, .svg-icon.sort {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.checkmate input:checked + svg.checkbox .checkmark {
|
||||
.checkmate input:checked + svg.checkbox .checkmark,
|
||||
svg.checkbox-enabled .checkmark {
|
||||
fill: var(--checkbox-icon-color);
|
||||
}
|
||||
|
||||
|
@ -693,6 +692,9 @@ a svg, .svg-icon.sort {
|
|||
height: var(--toolbar-height);
|
||||
padding: 4px 0;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#tools-wrapper:not(.hidden) + #installed {
|
||||
|
@ -702,20 +704,85 @@ a svg, .svg-icon.sort {
|
|||
top: calc(var(--header-height) + var(--toolbar-height));
|
||||
}
|
||||
|
||||
#tools-wrapper .select-resizer {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.manage-row {
|
||||
padding: 2px 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-end;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#filters-wrapper,
|
||||
#bulk-filter-count {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
#filters-wrapper input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#filters-wrapper svg {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.manage-row label,
|
||||
.manage-row button,
|
||||
.manage-row select {
|
||||
min-width: 2em;
|
||||
height: 24px;
|
||||
max-height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 0 6px;
|
||||
margin: 0 2px;
|
||||
vertical-align: middle;
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#filters-wrapper label:not(.checked):hover {
|
||||
background-color: var(--tools-bg-hover);
|
||||
}
|
||||
|
||||
.manage-row select {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.search-filter .svg-icon,
|
||||
.search-filter span {
|
||||
pointer-events: none;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.button-group label:first-child {
|
||||
margin-right: 0;
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.button-group label:last-child {
|
||||
margin-left: 0;
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.manage-row button,
|
||||
.search-filter {
|
||||
background: #e0e1e2;
|
||||
border: 1px #9e9e9e solid;
|
||||
}
|
||||
|
||||
.search-filter.checked,
|
||||
.search-filter.checked:hover {
|
||||
background-color: var(--label-usercss-bg-color);
|
||||
color: #fff;
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
#search-wrapper {
|
||||
|
@ -723,7 +790,8 @@ a svg, .svg-icon.sort {
|
|||
}
|
||||
|
||||
#search {
|
||||
width: calc(100% - var(--entry-icon-size) * 1.4);
|
||||
width: 100%;
|
||||
height: 2.2em;
|
||||
}
|
||||
|
||||
#search-help {
|
||||
|
@ -751,7 +819,7 @@ a svg, .svg-icon.sort {
|
|||
display: none !important;
|
||||
}
|
||||
|
||||
.active #filters-stats,
|
||||
#filters-stats,
|
||||
#bulk-filter-count:not(:empty) {
|
||||
background-color: var(--checked-count-bg-color);
|
||||
border-color: var(--checked-count-bg-color);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* global messageBox UI filtersSelector filterAndAppend
|
||||
/* global messageBox UI handleEvent filtersSelector filterAndAppend
|
||||
sorter $ $$ $create API onDOMready scrollElementIntoView t chromeLocal */
|
||||
/* exported handleUpdateInstalled */
|
||||
/* exported handleUpdateInstalled resetUpdates */
|
||||
'use strict';
|
||||
|
||||
let updateTimer;
|
||||
|
@ -27,6 +27,15 @@ function applyUpdateAll() {
|
|||
});
|
||||
}
|
||||
|
||||
function resetUpdates() {
|
||||
$('#check-all-updates-force').classList.add('hidden');
|
||||
$('#apply-all-updates').classList.add('hidden');
|
||||
$('#update-history').classList.add('hidden');
|
||||
const checkbox = $('#only-updates');
|
||||
checkbox.checked = false;
|
||||
checkbox.parentElement.classList.add('hidden');
|
||||
}
|
||||
|
||||
|
||||
function checkUpdateBulk() {
|
||||
clearTimeout(updateTimer);
|
||||
|
@ -120,6 +129,7 @@ function checkUpdate(entry, {single} = {}) {
|
|||
function reportUpdateState({updated, style, error, STATES}) {
|
||||
const isCheckAll = document.body.classList.contains('update-in-progress');
|
||||
const entry = $(UI.ENTRY_ID_PREFIX + style.id);
|
||||
if (!entry) return;
|
||||
const newClasses = new Map([
|
||||
/*
|
||||
When a style is updated/installed, handleUpdateInstalled() clears "updatable"
|
||||
|
@ -138,7 +148,10 @@ function reportUpdateState({updated, style, error, STATES}) {
|
|||
if (updated) {
|
||||
newClasses.set('can-update', true);
|
||||
entry.updatedCode = style;
|
||||
$('#only-updates').classList.remove('hidden');
|
||||
const onlyUpdates = $('#only-updates');
|
||||
onlyUpdates.parentElement.classList.remove('hidden');
|
||||
onlyUpdates.checked = true;
|
||||
onlyUpdates.change();
|
||||
} else if (!entry.classList.contains('can-update')) {
|
||||
const same = (
|
||||
error === STATES.SAME_MD5 ||
|
||||
|
@ -202,16 +215,15 @@ function reportUpdateState({updated, style, error, STATES}) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function renderUpdatesOnlyFilter({show, check} = {}) {
|
||||
const numUpdatable = $$('.can-update').length;
|
||||
const mightUpdate = numUpdatable > 0 || $('.update-problem');
|
||||
const checkbox = $('#only-updates input');
|
||||
const checkbox = $('#only-updates');
|
||||
show = show !== undefined ? show : mightUpdate;
|
||||
check = check !== undefined ? show && check : checkbox.checked && mightUpdate;
|
||||
|
||||
$('#only-updates').classList.toggle('hidden', !show);
|
||||
checkbox.checked = check && show;
|
||||
checkbox.checked = check;
|
||||
checkbox.parentElement.classList.toggle('hidden', !show);
|
||||
checkbox.dispatchEvent(new Event('change'));
|
||||
|
||||
const btnApply = $('#apply-all-updates');
|
||||
|
|
Loading…
Reference in New Issue
Block a user