add multi-column mode option
This commit is contained in:
parent
6979958908
commit
ef998e423e
|
@ -762,6 +762,9 @@
|
|||
"message": "Number of applies-to items",
|
||||
"description": "Label for the numeric input box to limit max number of applies-to targets in the new UI on manage page"
|
||||
},
|
||||
"manageMinColumnWidth": {
|
||||
"message": "Minimum column width (in pixels; 9999 disables multi-column mode)"
|
||||
},
|
||||
"manageNewStyleAsUsercss": {
|
||||
"message": "as Usercss",
|
||||
"description": "VERY SHORT label for the checkbox next to the 'Write new style' button in the style manager"
|
||||
|
|
|
@ -37,7 +37,7 @@ async function InjectionOrder(show, el, selector) {
|
|||
parts.name.href = '/edit.html?id=' + style.id;
|
||||
parts.name.textContent = style.name;
|
||||
return Object.assign(entry.cloneNode(true), {
|
||||
styleNameLowerCase: style.name.toLocaleLowerCase(),
|
||||
styleNameLC: style.name.toLocaleLowerCase(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -314,9 +314,7 @@ function setupLivePrefs(ids) {
|
|||
function getValue(el) {
|
||||
const type = el.dataset.valueType || el.type;
|
||||
return type === 'checkbox' ? el.checked :
|
||||
// https://stackoverflow.com/questions/18062069/why-does-valueasnumber-return-nan-as-a-value
|
||||
// valueAsNumber is not applicable for input[text/radio] or select
|
||||
type === 'number' ? Number(el.value) :
|
||||
type === 'number' ? parseFloat(el.value) :
|
||||
el.value;
|
||||
}
|
||||
function isSame(el, oldValue, value) {
|
||||
|
@ -465,6 +463,7 @@ prefs.ready.then(() => {
|
|||
const max = (innerWidth < 850 ? screen.availWidth : innerWidth) / 3;
|
||||
width = Math.round(Math.max(200, Math.min(max, Number(width) || 0)));
|
||||
$.root.style.setProperty('--header-width', width + 'px');
|
||||
dom.HWval = width;
|
||||
return width;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
'manage.actions.expanded': true,
|
||||
'manage.backup.expanded': true,
|
||||
'manage.filters.expanded': true,
|
||||
'manage.minColumnWidth': 750,
|
||||
// the new compact layout doesn't look good on Android yet
|
||||
'manage.newUI': true,
|
||||
'manage.newUI.favicons': false, // show favicons for the sites in applies-to
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
<a target="_blank" class="homepage"></a>
|
||||
</h2>
|
||||
<p class="applies-to">
|
||||
<span class="style-info" data-type="age"></span>.
|
||||
<label i18n="genericSize">:</label><span class="style-info" data-type="size"></span>.
|
||||
<label i18n="appliesDisplay"></label>
|
||||
<span class="targets"></span>
|
||||
</p>
|
||||
|
@ -29,10 +31,8 @@
|
|||
<button class="disable" i18n="disableStyleLabel"></button>
|
||||
<button class="delete" i18n="deleteStyleLabel"></button>
|
||||
<button class="check-update" i18n="checkForUpdate"></button>
|
||||
<button class="update" i18n="installUpdate"></button>
|
||||
<button class="update" i18n="installUpdate" hidden></button>
|
||||
<button class="configure-usercss" i18n="configureStyle"></button>
|
||||
<span class="style-info" data-type="size"></span>
|
||||
<span class="style-info" data-type="age"></span>
|
||||
<span class="update-note"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -49,9 +49,9 @@
|
|||
|
||||
<span class="style-info" data-type="version"></span>
|
||||
</a>
|
||||
<a target="_blank" class="homepage" tabindex="0"></a>
|
||||
</h2>
|
||||
<p class="actions">
|
||||
<a target="_blank" class="homepage" tabindex="0"></a>
|
||||
<a class="delete" i18n="title:deleteStyleLabel" tabindex="0">
|
||||
<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
|
||||
|
@ -391,6 +391,7 @@
|
|||
</template>
|
||||
|
||||
<link rel="stylesheet" href="manage/manage.css">
|
||||
<link rel="stylesheet" href="manage/manage-newui.css" id="newUI">
|
||||
<script src="js/dark-themer.js"></script> <!-- must be last in HEAD to avoid FOUC -->
|
||||
</head>
|
||||
|
||||
|
|
|
@ -130,7 +130,9 @@ const Events = {
|
|||
},
|
||||
|
||||
name(event, entry) {
|
||||
if (newUI.enabled) Events.edit(event, entry);
|
||||
if (newUI.enabled && !event.target.closest('.homepage')) {
|
||||
Events.edit(event, entry);
|
||||
}
|
||||
},
|
||||
|
||||
toggle(event, entry) {
|
||||
|
@ -193,7 +195,7 @@ function handleUpdate(style, {reason, method} = {}) {
|
|||
}
|
||||
entry = entry || createStyleElement(styleToDummyEntry(style));
|
||||
if (oldEntry) {
|
||||
if (oldEntry.styleNameLowerCase === entry.styleNameLowerCase) {
|
||||
if (oldEntry.styleNameLC === entry.styleNameLC) {
|
||||
installed.replaceChild(entry, oldEntry);
|
||||
} else {
|
||||
oldEntry.remove();
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
let found;
|
||||
for (const entry of rotated || entries) {
|
||||
if (entry.classList.contains('hidden')) continue;
|
||||
const name = entry.styleNameLowerCase;
|
||||
const name = entry.styleNameLC;
|
||||
const pos = name.indexOf(text);
|
||||
if (pos === 0) {
|
||||
found = entry;
|
||||
|
@ -73,7 +73,7 @@
|
|||
if (found && found !== focusedEntry) {
|
||||
focusedEntry = found;
|
||||
focusedLink = $('a', found);
|
||||
focusedName = found.styleNameLowerCase;
|
||||
focusedName = found.styleNameLC;
|
||||
scrollElementIntoView(found, {invalidMarginRatio: .25});
|
||||
animateElement(found, 'highlight-quick');
|
||||
replaceInlineStyle({
|
||||
|
|
278
manage/manage-newui.css
Normal file
278
manage/manage-newui.css
Normal file
|
@ -0,0 +1,278 @@
|
|||
.disabled.entry .svg-icon {
|
||||
color: var(--c50);
|
||||
fill: var(--c80);
|
||||
font-weight: normal;
|
||||
transition: color .5s .1s, fill .5s .1s;
|
||||
}
|
||||
#installed {
|
||||
margin-top: .75rem;
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
.entry {
|
||||
padding: 0 .5em;
|
||||
display: flex;
|
||||
border: none;
|
||||
}
|
||||
.entry.odd {
|
||||
background-color: rgba(128, 128, 128, 0.05);
|
||||
}
|
||||
.entry > * {
|
||||
padding: .5rem 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.entry .actions {
|
||||
position: relative;
|
||||
}
|
||||
.style-info[data-type=size],
|
||||
.style-info[data-type=age] {
|
||||
color: var(--c50);
|
||||
justify-content: end;
|
||||
}
|
||||
.style-info[data-type=age] {
|
||||
flex: 0 0 4ch;
|
||||
}
|
||||
.style-info[data-type=size] {
|
||||
flex: 0 0 var(--size-width);
|
||||
}
|
||||
.style-info[data-type=version] {
|
||||
color: var(--c40);
|
||||
padding-left: .5em;
|
||||
font-weight: normal;
|
||||
}
|
||||
.style-info[data-type=version][data-is-date],
|
||||
.style-info[data-type=version][data-value=""],
|
||||
.style-info[data-type=version][data-value="1.0.0"] {
|
||||
display: none;
|
||||
}
|
||||
.entry input[type="checkbox"]:not(.slider) {
|
||||
pointer-events: all;
|
||||
}
|
||||
.style-name {
|
||||
font-size: 14px;
|
||||
padding-left: var(--name-padding-left);
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
justify-content: space-between;
|
||||
flex: 0 0 var(--name-width);
|
||||
min-width: 25%;
|
||||
max-width: 50%;
|
||||
}
|
||||
#installed[style*="--num-targets:0"] .style-name {
|
||||
max-width: none;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.checkmate {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.style-name-link {
|
||||
width: 100%;
|
||||
}
|
||||
.entry .style-name:hover::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(to right, hsla(180, 50%, 30%, 0.2), hsla(180, 20%, 10%, 0.05) 50%, transparent);
|
||||
pointer-events: none;
|
||||
}
|
||||
.entry.enabled .style-name:hover .style-name-link {
|
||||
color: var(--accent-1);
|
||||
}
|
||||
.homepage {
|
||||
margin-top: -3px;
|
||||
}
|
||||
.actions {
|
||||
flex: 0 0 calc(3 * (var(--action-size) + var(--action-margin)));
|
||||
flex-wrap: nowrap;
|
||||
z-index: 100;
|
||||
}
|
||||
.actions > * {
|
||||
width: var(--action-size);
|
||||
height: var(--action-size);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.updater-icons > * {
|
||||
transition: opacity 1s;
|
||||
display: none;
|
||||
}
|
||||
.entry .svg-icon {
|
||||
fill: var(--c60);
|
||||
}
|
||||
.entry:hover .svg-icon {
|
||||
fill: var(--c40);
|
||||
}
|
||||
.entry .svg-icon.checked,
|
||||
.entry:hover .svg-icon.checked,
|
||||
.entry:hover .svg-icon:hover {
|
||||
fill: var(--fg);
|
||||
}
|
||||
.checking-update .check-update {
|
||||
opacity: 0;
|
||||
display: inline-block;
|
||||
pointer-events: none;
|
||||
}
|
||||
.can-update .update,
|
||||
.no-update:not(.update-problem):not(.update-done) .up-to-date,
|
||||
.no-update.update-problem .check-update,
|
||||
.update-done .updated {
|
||||
display: inline-block;
|
||||
}
|
||||
.up-to-date svg,
|
||||
.updated svg {
|
||||
cursor: auto;
|
||||
}
|
||||
.update-done .updated svg {
|
||||
top: -4px;
|
||||
position: relative;
|
||||
filter: drop-shadow(0 5px 0 currentColor);
|
||||
}
|
||||
.can-update .update,
|
||||
.no-update.update-problem .check-update {
|
||||
cursor: pointer;
|
||||
}
|
||||
.can-update[data-details$="locally edited"] .update svg,
|
||||
.update-problem .check-update svg {
|
||||
fill: #ef6969;
|
||||
}
|
||||
.can-update[data-details$="locally edited"]:hover .update svg,
|
||||
.entry.update-problem:hover .check-update svg {
|
||||
fill: #fd4040;
|
||||
}
|
||||
.can-update[data-details$="locally edited"]:hover .update svg:hover,
|
||||
.entry.update-problem:hover .check-update svg:hover {
|
||||
fill: red;
|
||||
}
|
||||
.updater-icons > :not(.check-update):after {
|
||||
content: attr(title);
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: max-content;
|
||||
max-width: 25vw;
|
||||
padding: 1ex 1.5ex;
|
||||
border: 1px solid #ded597;
|
||||
background-color: #fffbd6;
|
||||
border-radius: 4px;
|
||||
box-shadow: 2px 3px 10px rgba(0,0,0,.25);
|
||||
font-size: 90%;
|
||||
animation: fadeout 10s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
.update-problem .check-update:after {
|
||||
background-color: red;
|
||||
border: 1px solid #d40000;
|
||||
color: white;
|
||||
animation: none;
|
||||
}
|
||||
.can-update .update:after {
|
||||
animation: none;
|
||||
}
|
||||
.can-update:not([data-details$="locally edited"]) .update:after {
|
||||
background-color: #c0fff0;
|
||||
border: 1px solid #89cac9;
|
||||
}
|
||||
.applies-to {
|
||||
padding: .25em 0 .25em 1em;
|
||||
flex-grow: 999;
|
||||
}
|
||||
#installed[style*="--num-targets:0"] .applies-to {
|
||||
display: none;
|
||||
}
|
||||
.targets {
|
||||
overflow: hidden;
|
||||
max-height: calc(var(--num-targets) * 18px);
|
||||
width: 100%;
|
||||
}
|
||||
.applies-to.expanded .targets {
|
||||
max-height: none;
|
||||
}
|
||||
.target {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-right: 1em;
|
||||
line-height: 18px;
|
||||
width: 0;
|
||||
min-width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.applies-to:not(.has-more) .expander {
|
||||
display: none;
|
||||
}
|
||||
.target:hover {
|
||||
background-color: inherit;
|
||||
}
|
||||
.target img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
margin: -1px 4px 0 -20px;
|
||||
transition: opacity .5s, filter .5s;
|
||||
/* workaround for the buggy CSS filter: images in the hidden overflow are shown on Mac */
|
||||
backface-visibility: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
.favicons-grayed .target img {
|
||||
filter: grayscale(1);
|
||||
opacity: .25;
|
||||
}
|
||||
.has-favicons .target {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.has-favicons .target img[src] {
|
||||
visibility: visible;
|
||||
}
|
||||
.entry:hover .target img {
|
||||
opacity: 1;
|
||||
filter: none;
|
||||
}
|
||||
.target b::after {
|
||||
content: '?';
|
||||
margin: -2px 4px 0 -20px;
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
background: var(--c85);
|
||||
width: 16px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
color: var(--bg);
|
||||
}
|
||||
@media (max-width: 850px) {
|
||||
.entry {
|
||||
padding: 0;
|
||||
}
|
||||
.entry .checkmate {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
.entry .style-name {
|
||||
text-indent: unset;
|
||||
}
|
||||
.entry .actions {
|
||||
width: 104px;
|
||||
padding: .5rem 0 .5rem 6px;
|
||||
}
|
||||
.entry .applies-to {
|
||||
padding: .25rem .5rem .25rem 0;
|
||||
}
|
||||
.entry .target {
|
||||
max-width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
.style-name-link::after {
|
||||
text-indent: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
.entry > .style-info {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
:root {
|
||||
--name-padding-left: 20px;
|
||||
--name-padding-right: 40px;
|
||||
--actions-width: 75px;
|
||||
--name-width: 30ch;
|
||||
--size-width: 4ch;
|
||||
--action-size: 20px;
|
||||
--action-margin: 6px;
|
||||
}
|
||||
body {
|
||||
/* Fill the entire viewport to enable json import via drag'n'drop */
|
||||
|
@ -124,17 +126,21 @@ a:hover {
|
|||
}
|
||||
|
||||
#installed {
|
||||
position: relative;
|
||||
padding-left: var(--header-width);
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
align-self: start;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.entry {
|
||||
margin: 0;
|
||||
padding: 1.25em 2em;
|
||||
border-top: 1px solid var(--c85);
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
width: calc(100% / var(--columns, 1));
|
||||
}
|
||||
|
||||
.entry:first-child {
|
||||
|
@ -158,15 +164,13 @@ a:hover {
|
|||
margin-top: .25em;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.style-name a, .style-edit-link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.style-name span,
|
||||
.applies-to {
|
||||
overflow-wrap: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.applies-to,
|
||||
.actions {
|
||||
padding-left: 15px;
|
||||
|
@ -181,11 +185,10 @@ a:hover {
|
|||
|
||||
.actions > * {
|
||||
margin-bottom: .25rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.actions > *:not(:last-child) {
|
||||
margin-right: .25rem;
|
||||
margin-right: var(--action-margin);
|
||||
}
|
||||
|
||||
.applies-to label {
|
||||
|
@ -240,8 +243,7 @@ a:hover {
|
|||
}
|
||||
|
||||
.disabled h2 .style-name-link,
|
||||
.disabled .applies-to,
|
||||
.newUI .disabled.entry .svg-icon {
|
||||
.disabled .applies-to {
|
||||
color: var(--c50);
|
||||
fill: var(--c80);
|
||||
font-weight: normal;
|
||||
|
@ -319,56 +321,8 @@ a:hover {
|
|||
|
||||
/* compact layout */
|
||||
|
||||
.newUI #installed {
|
||||
display: table;
|
||||
margin-top: .75rem;
|
||||
margin-bottom: .75rem;
|
||||
}
|
||||
|
||||
.newUI .entry {
|
||||
display: table-row;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.newUI .entry.odd {
|
||||
background-color: rgba(128, 128, 128, 0.05);
|
||||
}
|
||||
|
||||
.newUI .entry > * {
|
||||
padding: .5rem 0;
|
||||
margin: 0;
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.newUI .entry .actions {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.style-info[data-type=version] {
|
||||
color: var(--c40);
|
||||
padding-left: .5em;
|
||||
font-weight: normal;
|
||||
}
|
||||
.newUI .style-info[data-type=version][data-is-date],
|
||||
.newUI .style-info[data-type=version][data-value=""],
|
||||
.newUI .style-info[data-type=version][data-value="1.0.0"] {
|
||||
display: none;
|
||||
}
|
||||
.newUI .entry .style-info[data-type=size],
|
||||
.newUI .entry .style-info[data-type=age] {
|
||||
color: var(--c50);
|
||||
text-align: right;
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
/************ checkbox & select************/
|
||||
|
||||
#newUIoptions > div, #newUIoptions > label {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.filter-selection {
|
||||
position: relative;
|
||||
left: -9px;
|
||||
|
@ -393,10 +347,6 @@ a:hover {
|
|||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.newUI #newUIoptions > label {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.filter-selection select {
|
||||
height: 18px;
|
||||
border: none;
|
||||
|
@ -439,7 +389,7 @@ a:hover {
|
|||
|
||||
.entry .checkmate {
|
||||
vertical-align: middle;
|
||||
margin: -2px 1ex 0 0;
|
||||
margin-right: 1ch;
|
||||
}
|
||||
|
||||
#manage-text {
|
||||
|
@ -456,179 +406,10 @@ a:hover {
|
|||
margin: 0 .5em;
|
||||
}
|
||||
|
||||
.newUI .entry input[type="checkbox"]:not(.slider) {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.newUI .style-name {
|
||||
font-size: 14px;
|
||||
padding-left: var(--name-padding-left);
|
||||
padding-right: var(--name-padding-right);
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.newUI .entry .style-name:hover::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(to right, hsla(180, 50%, 30%, 0.2), hsla(180, 20%, 10%, 0.05) 50%, transparent);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.newUI .entry.enabled .style-name:hover .style-name-link {
|
||||
color: var(--accent-1);
|
||||
}
|
||||
|
||||
.newUI .style-name:after {
|
||||
text-indent: 1.2rem;
|
||||
}
|
||||
|
||||
.newUI .actions:after {
|
||||
text-indent: -25px;
|
||||
}
|
||||
|
||||
.newUI .actions .homepage[href=""] {
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.newUI .actions {
|
||||
width: var(--actions-width);
|
||||
height: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.newUI .actions > * {
|
||||
margin: 0 6px 0 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.newUI .updater-icons > * {
|
||||
transition: opacity 1s;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.newUI .entry .svg-icon {
|
||||
fill: var(--c60);
|
||||
}
|
||||
|
||||
.newUI .entry:hover .svg-icon {
|
||||
fill: var(--c40);
|
||||
}
|
||||
|
||||
button .svg-icon,
|
||||
.newUI .entry .svg-icon.checked,
|
||||
.newUI .entry:hover .svg-icon.checked,
|
||||
.newUI .entry:hover .svg-icon:hover {
|
||||
button .svg-icon {
|
||||
fill: var(--fg);
|
||||
}
|
||||
|
||||
.newUI .checking-update .check-update {
|
||||
opacity: 0;
|
||||
display: inline-block;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.newUI .can-update .update,
|
||||
.newUI .no-update:not(.update-problem):not(.update-done) .up-to-date,
|
||||
.newUI .no-update.update-problem .check-update,
|
||||
.newUI .update-done .updated {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.newUI .up-to-date svg,
|
||||
.newUI .updated svg {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.newUI .update-done .updated svg {
|
||||
top: -4px;
|
||||
position: relative;
|
||||
filter: drop-shadow(0 5px 0 currentColor);
|
||||
}
|
||||
|
||||
.newUI .can-update .update,
|
||||
.newUI .no-update.update-problem .check-update {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.newUI .can-update[data-details$="locally edited"] .update svg,
|
||||
.newUI .update-problem .check-update svg {
|
||||
fill: #ef6969;
|
||||
}
|
||||
|
||||
.newUI .can-update[data-details$="locally edited"]:hover .update svg,
|
||||
.newUI .entry.update-problem:hover .check-update svg {
|
||||
fill: #fd4040;
|
||||
}
|
||||
|
||||
.newUI .can-update[data-details$="locally edited"]:hover .update svg:hover,
|
||||
.newUI .entry.update-problem:hover .check-update svg:hover {
|
||||
fill: red;
|
||||
}
|
||||
|
||||
.newUI .actions {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.newUI .updater-icons > :not(.check-update):after {
|
||||
content: attr(title);
|
||||
position: absolute;
|
||||
margin-top: 18px;
|
||||
margin-left: -36px;
|
||||
padding: 1ex 1.5ex;
|
||||
border: 1px solid #ded597;
|
||||
background-color: #fffbd6;
|
||||
border-radius: 4px;
|
||||
box-shadow: 2px 3px 10px rgba(0,0,0,.25);
|
||||
font-size: 90%;
|
||||
animation: fadeout 10s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.newUI .update-problem .check-update:after {
|
||||
background-color: red;
|
||||
border: 1px solid #d40000;
|
||||
color: white;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.newUI .can-update .update:after {
|
||||
animation: none;
|
||||
}
|
||||
|
||||
.newUI .can-update:not([data-details$="locally edited"]) .update:after {
|
||||
background-color: #c0fff0;
|
||||
border: 1px solid #89cac9;
|
||||
}
|
||||
|
||||
.newUI .applies-to {
|
||||
padding-top: .25rem;
|
||||
padding-bottom: .25rem;
|
||||
}
|
||||
.newUI .targets {
|
||||
overflow: hidden;
|
||||
max-height: calc(var(--num-targets) * 18px);
|
||||
}
|
||||
.newUI .applies-to.expanded .targets {
|
||||
max-height: none;
|
||||
}
|
||||
.newUI .target {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: calc(75vw - var(--header-width) - var(--actions-width) - var(--name-padding-left) - var(--name-padding-right) - 6rem);
|
||||
box-sizing: border-box;
|
||||
padding-right: 1rem;
|
||||
line-height: 18px;
|
||||
}
|
||||
.expander {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
|
@ -643,54 +424,6 @@ button .svg-icon,
|
|||
transform: rotate(180deg);
|
||||
transform-origin: 8px 8px;
|
||||
}
|
||||
.newUI .applies-to:not(.has-more) .expander {
|
||||
display: none;
|
||||
}
|
||||
.newUI .target:hover {
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
.newUI .target img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
margin: -1px 4px 0 -20px;
|
||||
transition: opacity .5s, filter .5s;
|
||||
/* workaround for the buggy CSS filter: images in the hidden overflow are shown on Mac */
|
||||
backface-visibility: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.newUI .favicons-grayed .target img {
|
||||
filter: grayscale(1);
|
||||
opacity: .25;
|
||||
}
|
||||
|
||||
.newUI .has-favicons .target {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.newUI .has-favicons .target img[src] {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.newUI .entry:hover .target img {
|
||||
opacity: 1;
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.newUI .target b::after {
|
||||
content: '?';
|
||||
margin: -2px 4px 0 -20px;
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
background: var(--c85);
|
||||
width: 16px;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
color: var(--bg);
|
||||
}
|
||||
|
||||
/* Default, no update buttons */
|
||||
.updater-icons .update,
|
||||
|
@ -1032,12 +765,6 @@ button .svg-icon,
|
|||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px) {
|
||||
.newUI .entry > .style-info {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 850px) {
|
||||
:root {
|
||||
--name-padding-left: 34px;
|
||||
|
@ -1058,7 +785,7 @@ button .svg-icon,
|
|||
left: 3.75rem;
|
||||
}
|
||||
|
||||
html:not(.newUI) .applies-to {
|
||||
.oldUI .applies-to {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
|
@ -1066,10 +793,6 @@ button .svg-icon,
|
|||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.newUI .entry .actions {
|
||||
padding-right: 30px
|
||||
}
|
||||
|
||||
#search-wrapper,
|
||||
#sort-wrapper,
|
||||
#header summary {
|
||||
|
@ -1127,41 +850,6 @@ button .svg-icon,
|
|||
margin-top: 0;
|
||||
padding-bottom: .25rem;
|
||||
}
|
||||
|
||||
.newUI .entry {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.newUI .entry .checkmate {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.newUI .entry .style-name {
|
||||
text-indent: unset;
|
||||
}
|
||||
|
||||
.newUI .entry .actions {
|
||||
width: 104px;
|
||||
padding: .5rem 0 .5rem 6px;
|
||||
}
|
||||
|
||||
.newUI .entry .applies-to {
|
||||
padding: .25rem .5rem .25rem 0;
|
||||
}
|
||||
|
||||
.newUI .entry .target {
|
||||
max-width: 100%;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.newUI .style-name-link::after {
|
||||
text-indent: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (-moz-appearance: none) {
|
||||
|
|
|
@ -28,9 +28,16 @@ const newUI = {
|
|||
Object.assign(newUI, {
|
||||
ids: Object.keys(newUI),
|
||||
prefKeyForId: id => `manage.newUI.${id}`.replace(/\.enabled$/, ''),
|
||||
readPrefs(dest = newUI, cb) {
|
||||
for (const id of newUI.ids) {
|
||||
const val = dest[id] = prefs.get(newUI.prefKeyForId(id));
|
||||
if (cb) cb(id, val);
|
||||
}
|
||||
},
|
||||
renderClass: () => {
|
||||
$.rootCL.toggle('newUI', newUI.enabled);
|
||||
$.rootCL.toggle('oldUI', !newUI.enabled);
|
||||
$('#newUI').media = newUI.enabled ? '' : '?';
|
||||
},
|
||||
hasFavs: () => newUI.enabled && newUI.favicons,
|
||||
badFavsKey: 'badFavs',
|
||||
|
@ -41,9 +48,7 @@ Object.assign(newUI, {
|
|||
},
|
||||
});
|
||||
// ...read the actual values
|
||||
for (const id of newUI.ids) {
|
||||
newUI[id] = prefs.get(newUI.prefKeyForId(id));
|
||||
}
|
||||
newUI.readPrefs();
|
||||
newUI.renderClass();
|
||||
|
||||
(async function init() {
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
/* global URLS debounce getOwnTab isEmptyObj sessionStore stringAsRegExp */// toolbox.js
|
||||
/* global filterAndAppend */// filters.js
|
||||
/* global installed newUI */// manage.js
|
||||
/* global prefs */
|
||||
/* global sorter */
|
||||
/* global t */// localization.js
|
||||
'use strict';
|
||||
|
@ -18,6 +17,7 @@ const AGES = [
|
|||
[Infinity, 'y', t('dateAbbrYear', '\x01')],
|
||||
];
|
||||
const groupThousands = num => `${num}`.replace(/\d(?=(\d{3})+$)/g, '$&\xA0');
|
||||
const renderSize = size => groupThousands(Math.round(size / 1024)) + 'k';
|
||||
|
||||
(() => {
|
||||
const proto = HTMLImageElement.prototype;
|
||||
|
@ -79,7 +79,7 @@ function calcObjSize(obj) {
|
|||
Object.entries(obj).reduce((sum, [k, v]) => sum + k.length + calcObjSize(v), 0);
|
||||
}
|
||||
|
||||
function createStyleElement({styleMeta: style, styleNameLowerCase: nameLC, styleSize: size}) {
|
||||
function createStyleElement({styleMeta: style, styleNameLC: nameLC, styleSize: size}) {
|
||||
// query the sub-elements just once, then reuse the references
|
||||
if ((elementParts || {}).newUI !== newUI.enabled) {
|
||||
const entry = t.template[newUI.enabled ? 'styleNewUI' : 'style'].cloneNode(true);
|
||||
|
@ -105,7 +105,6 @@ function createStyleElement({styleMeta: style, styleNameLowerCase: nameLC, style
|
|||
},
|
||||
oldConfigure: !newUI.enabled && $('.configure-usercss', entry),
|
||||
oldCheckUpdate: !newUI.enabled && $('.check-update', entry),
|
||||
oldUpdate: !newUI.enabled && $('.update', entry),
|
||||
};
|
||||
}
|
||||
const parts = elementParts;
|
||||
|
@ -126,12 +125,11 @@ function createStyleElement({styleMeta: style, styleNameLowerCase: nameLC, style
|
|||
}
|
||||
createAgeText(parts.infoAge, style);
|
||||
parts.infoSize.dataset.value = Math.log10(size || 1) >> 0; // for CSS to target big/small styles
|
||||
parts.infoSize.textContent = groupThousands(Math.round(size / 1024)) + 'k';
|
||||
parts.infoSize.textContent = renderSize(size);
|
||||
parts.infoSize.title = `${t('genericSize')}: ${groupThousands(size)} B`;
|
||||
if (!newUI.enabled) {
|
||||
parts.oldConfigure.classList.toggle('hidden', !configurable);
|
||||
parts.oldCheckUpdate.classList.toggle('hidden', !style.updateUrl);
|
||||
parts.oldUpdate.classList.toggle('hidden', !style.updateUrl);
|
||||
}
|
||||
|
||||
// clear the code to free up some memory
|
||||
|
@ -142,7 +140,7 @@ function createStyleElement({styleMeta: style, styleNameLowerCase: nameLC, style
|
|||
const entry = parts.entry.cloneNode(true);
|
||||
entry.id = ENTRY_ID_PREFIX_RAW + style.id;
|
||||
entry.styleId = style.id;
|
||||
entry.styleNameLowerCase = nameLC;
|
||||
entry.styleNameLC = nameLC;
|
||||
entry.styleMeta = style;
|
||||
entry.styleSize = size;
|
||||
entry.className = parts.entryClassBase + ' ' +
|
||||
|
@ -166,6 +164,12 @@ function createStyleElement({styleMeta: style, styleNameLowerCase: nameLC, style
|
|||
}
|
||||
|
||||
function createTargetsElement({entry, expanded, style = entry.styleMeta}) {
|
||||
const maxTargets = expanded ? 1000 : newUI.enabled ? newUI.targets : 10;
|
||||
if (!maxTargets) {
|
||||
entry._numTargets = 0;
|
||||
return;
|
||||
}
|
||||
const displayed = new Set();
|
||||
const entryTargets = $('.targets', entry);
|
||||
const expanderCls = $('.applies-to', entry).classList;
|
||||
const targets = elementParts.targets.cloneNode(true);
|
||||
|
@ -173,8 +177,6 @@ function createTargetsElement({entry, expanded, style = entry.styleMeta}) {
|
|||
let el = entryTargets.firstElementChild;
|
||||
let numTargets = 0;
|
||||
let allTargetsRendered = true;
|
||||
const maxTargets = expanded ? 1000 : newUI.enabled ? newUI.targets : 10;
|
||||
const displayed = new Set();
|
||||
for (const type of TARGET_TYPES) {
|
||||
for (const section of style.sections) {
|
||||
for (const targetValue of section[type] || []) {
|
||||
|
@ -352,26 +354,43 @@ function padLeft(val, width) {
|
|||
return ' '.repeat(Math.max(0, width - val.length)) + val;
|
||||
}
|
||||
|
||||
function fitNameColumn(styles) {
|
||||
const align = 1e9; // required by sort()
|
||||
const lengths = styles.map(s => align +
|
||||
(s = s.displayName || s.name || '').length +
|
||||
s.replace(/[^\u3000-\uFE00]+/g, '').length).sort(); // CJK glyphs are twice as wide
|
||||
const len = styles.length;
|
||||
const fringe = len * 5 / 100 | 0; // ignoring 5% of outliers at each end
|
||||
let avgName = 0;
|
||||
for (let i = fringe; i < len - fringe; i++) {
|
||||
avgName = Math.max(avgName, lengths[i] - align);
|
||||
}
|
||||
$.root.style.setProperty('--name-width', avgName + 'ch');
|
||||
}
|
||||
|
||||
function fitSizeColumn(entries) {
|
||||
const max = entries.reduce((res, e) => Math.max(res, e.styleSize), 0);
|
||||
$.root.style.setProperty('--size-width', renderSize(max).length + 'ch');
|
||||
}
|
||||
|
||||
function showStyles(styles = [], matchUrlIds) {
|
||||
const sorted = sorter.sort(styles.map(styleToDummyEntry));
|
||||
const dummies = styles.map(styleToDummyEntry);
|
||||
const sorted = sorter.sort(dummies);
|
||||
let index = 0;
|
||||
let firstRun = true;
|
||||
installed.dataset.total = styles.length;
|
||||
const scrollY = (history.state || {}).scrollY;
|
||||
const shouldRenderAll = scrollY > window.innerHeight || sessionStore.justEditedStyleId;
|
||||
const renderBin = document.createDocumentFragment();
|
||||
if (scrollY) {
|
||||
fitNameColumn(styles);
|
||||
fitSizeColumn(dummies);
|
||||
renderStyles();
|
||||
} else {
|
||||
requestAnimationFrame(renderStyles);
|
||||
}
|
||||
|
||||
function renderStyles() {
|
||||
const t0 = performance.now();
|
||||
while (index < sorted.length && (shouldRenderAll || performance.now() - t0 < 20)) {
|
||||
const info = sorted[index++];
|
||||
const entry = createStyleElement(info);
|
||||
if (matchUrlIds && !matchUrlIds.includes(info.style.id)) {
|
||||
while (index < sorted.length && (shouldRenderAll || performance.now() - t0 < 50)) {
|
||||
const entry = createStyleElement(sorted[index++]);
|
||||
if (matchUrlIds && !matchUrlIds.includes(entry.styleMeta.id)) {
|
||||
entry.classList.add('not-matching');
|
||||
}
|
||||
renderBin.appendChild(entry);
|
||||
|
@ -398,7 +417,7 @@ function styleToDummyEntry(style) {
|
|||
styleMeta: style,
|
||||
styleSize: calcObjSize(style),
|
||||
// sort case-insensitively the whole list then sort dupes like `Foo` and `foo` case-sensitively
|
||||
styleNameLowerCase: name.toLocaleLowerCase() + '\n' + name,
|
||||
styleNameLC: name.toLocaleLowerCase() + '\n' + name,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -407,13 +426,11 @@ function switchUI({styleOnly} = {}) {
|
|||
const current = {};
|
||||
const changed = {};
|
||||
let someChanged = false;
|
||||
for (const id of newUI.ids) {
|
||||
const value = prefs.get(newUI.prefKeyForId(id));
|
||||
newUI.readPrefs(current, (id, value) => {
|
||||
const valueChanged = value !== newUI[id] && (id === 'enabled' || current.enabled);
|
||||
current[id] = value;
|
||||
changed[id] = valueChanged;
|
||||
someChanged |= valueChanged;
|
||||
}
|
||||
});
|
||||
|
||||
if (!styleOnly && !someChanged) {
|
||||
return;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global $ $create messageBoxProxy */// dom.js
|
||||
/* global $ $create dom messageBoxProxy */// dom.js
|
||||
/* global installed */// manage.js
|
||||
/* global prefs */
|
||||
/* global t */// localization.js
|
||||
|
@ -6,6 +6,10 @@
|
|||
|
||||
const sorter = (() => {
|
||||
|
||||
const COL_MIN = 300; // same as options.html
|
||||
const COL_MAX = 9999; // same as options.html
|
||||
const COL_PROP = '--columns';
|
||||
|
||||
const sorterType = {
|
||||
alpha: (a, b) => a < b ? -1 : a === b ? 0 : 1,
|
||||
number: (a, b) => (a || 0) - (b || 0),
|
||||
|
@ -14,7 +18,7 @@ const sorter = (() => {
|
|||
const tagData = {
|
||||
title: {
|
||||
text: t('genericTitle'),
|
||||
parse: v => v.styleNameLowerCase,
|
||||
parse: v => v.styleNameLC,
|
||||
sorter: sorterType.alpha,
|
||||
},
|
||||
usercss: {
|
||||
|
@ -71,12 +75,13 @@ const sorter = (() => {
|
|||
const getPref = () => prefs.get(ID) || prefs.defaults[ID];
|
||||
|
||||
let columns = 1;
|
||||
let minWidth;
|
||||
|
||||
function init() {
|
||||
prefs.subscribe(ID, sorter.update);
|
||||
$('#sorter-help').onclick = showHelp;
|
||||
addOptions();
|
||||
updateColumnCount();
|
||||
prefs.subscribe('manage.minColumnWidth', updateColumnWidth, {runNow: true});
|
||||
}
|
||||
|
||||
function addOptions() {
|
||||
|
@ -172,21 +177,40 @@ const sorter = (() => {
|
|||
};
|
||||
|
||||
function updateColumnCount() {
|
||||
let newValue = 1;
|
||||
for (let el = $.root.lastElementChild;
|
||||
el.localName === 'style';
|
||||
el = el.previousElementSibling) {
|
||||
if (el.textContent.includes('--columns:')) {
|
||||
newValue = Math.max(1, getComputedStyle($.root).getPropertyValue('--columns') | 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (columns !== newValue) {
|
||||
columns = newValue;
|
||||
const useStyle = [].some.call($.root.children,
|
||||
el => el.tagName === 'STYLE' && el.textContent.includes(COL_PROP + ':'));
|
||||
const v = useStyle ? Math.max(1, getComputedStyle($.root).getPropertyValue(COL_PROP) >> 0)
|
||||
: minWidth ? onResize()
|
||||
: columns;
|
||||
if (columns !== v) {
|
||||
columns = v;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function updateColumnWidth(_, val) {
|
||||
minWidth = Math.max(val, COL_MIN);
|
||||
if (val < COL_MAX) {
|
||||
window.on('resize', onResize);
|
||||
} else {
|
||||
window.off('resize', onResize);
|
||||
$.root.style.removeProperty(COL_PROP);
|
||||
}
|
||||
sorter.updateStripes({onlyWhenColumnsChanged: true});
|
||||
}
|
||||
|
||||
function onResize(evt) {
|
||||
const c = Math.max(1, (window.innerWidth - dom.HWval) / minWidth >> 0);
|
||||
if (columns !== c) {
|
||||
$.root.style.setProperty(COL_PROP, c);
|
||||
if (evt) {
|
||||
columns = c;
|
||||
sorter.updateStripes();
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
async function showHelp(event) {
|
||||
event.preventDefault();
|
||||
messageBoxProxy.show({
|
||||
|
|
|
@ -240,6 +240,10 @@
|
|||
<div class="block">
|
||||
<h1 i18n="openManage"></h1>
|
||||
<div class="items">
|
||||
<label>
|
||||
<span i18n="manageMinColumnWidth"></span>
|
||||
<input id="manage.minColumnWidth" type="number" min="300" max="9999">
|
||||
</label>
|
||||
<label>
|
||||
<span i18n="manageNewUI"></span>
|
||||
<span class="onoffswitch">
|
||||
|
@ -268,7 +272,7 @@
|
|||
</label>
|
||||
<label>
|
||||
<span i18n="manageMaxTargets"></span>
|
||||
<input id="manage.newUI.targets" type="number" min="1" max="99">
|
||||
<input id="manage.newUI.targets" type="number" min="0" max="99">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue
Block a user