scrollable details + sticky header (#1400)

* shorten section labels in lint report
* `sectioned` class on html for sectioned editor
* fix scrollElementIntoView
This commit is contained in:
tophf 2022-02-14 22:19:20 +03:00 committed by GitHub
parent 225a2cec31
commit dd38856eda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 242 additions and 280 deletions

View File

@ -418,17 +418,15 @@
<summary><h2 i18n-text="sections"></h2></summary> <summary><h2 i18n-text="sections"></h2></summary>
<ol id="toc"></ol> <ol id="toc"></ol>
</details> </details>
<details id="lint" data-pref="editor.lint.expanded" class="hidden-unless-compact ignore-pref-if-compact"> <details id="lint" data-pref="editor.lint.expanded" class="ignore-pref-if-compact" hidden>
<summary> <summary>
<h2 i18n-text="linterIssues">: <span id="issue-count"></span> <h2><span i18n-text="linterIssues"></span><span id="issue-count"></span>
<a id="lint-help" class="svg-inline-wrapper intercepts-click" tabindex="0"> <a id="lint-help" class="svg-inline-wrapper intercepts-click" tabindex="0">
<svg class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg> <svg class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg>
</a> </a>
</h2> </h2>
</summary> </summary>
<div class="lint-scroll-container">
<div class="lint-report-container"></div> <div class="lint-report-container"></div>
</div>
</details> </details>
</div> </div>
<div id="header-resizer" i18n-title="headerResizerHint"></div> <div id="header-resizer" i18n-title="headerResizerHint"></div>

View File

@ -6,14 +6,7 @@
/* global initBeautifyButton */// beautify.js /* global initBeautifyButton */// beautify.js
/* global prefs */ /* global prefs */
/* global t */// localization.js /* global t */// localization.js
/* global /* global FIREFOX getOwnTab sessionStore tryJSONparse tryURL */// toolbox.js
FIREFOX
debounce
getOwnTab
sessionStore
tryJSONparse
tryURL
*/// toolbox.js
'use strict'; 'use strict';
/** /**
@ -39,7 +32,7 @@ const editor = {
cancel: () => location.assign('/manage.html'), cancel: () => location.assign('/manage.html'),
updateClass() { updateClass() {
document.documentElement.classList.toggle('is-new-style', !editor.style.id); $.rootCL.toggle('is-new-style', !editor.style.id);
}, },
updateTitle(isDirty = editor.dirty.isDirty()) { updateTitle(isDirty = editor.dirty.isDirty()) {
@ -56,9 +49,14 @@ const editor = {
const baseInit = (() => { const baseInit = (() => {
const domReady = waitForSelector('#sections'); const domReady = waitForSelector('#sections');
const mqCompact = matchMedia('(max-width: 850px)');
const toggleCompact = mq => $.rootCL.toggle('compact-layout', mq.matches);
mqCompact.on('change', toggleCompact);
toggleCompact(mqCompact);
return { return {
domReady, domReady,
mqCompact,
ready: Promise.all([ ready: Promise.all([
domReady, domReady,
loadStyle(), loadStyle(),
@ -94,7 +92,7 @@ const baseInit = (() => {
editor.style = style; editor.style = style;
editor.updateClass(); editor.updateClass();
editor.updateTitle(false); editor.updateTitle(false);
document.documentElement.classList.toggle('usercss', editor.isUsercss); $.rootCL.add(editor.isUsercss ? 'usercss' : 'sectioned');
sessionStore.justEditedStyleId = style.id || ''; sessionStore.justEditedStyleId = style.id || '';
// no such style so let's clear the invalid URL parameters // no such style so let's clear the invalid URL parameters
if (!style.id) history.replaceState({}, '', location.pathname); if (!style.id) history.replaceState({}, '', location.pathname);
@ -120,45 +118,6 @@ const baseInit = (() => {
} }
})(); })();
//#endregion
//#region init layout/resize
baseInit.domReady.then(() => {
let headerHeight;
detectLayout(true);
window.on('resize', () => detectLayout());
function detectLayout(now) {
const compact = window.innerWidth <= 850;
if (compact) {
document.body.classList.add('compact-layout');
if (!editor.isUsercss) {
if (now) fixedHeader();
else debounce(fixedHeader, 250);
window.on('scroll', fixedHeader, {passive: true});
}
} else {
document.body.classList.remove('compact-layout', 'fixed-header');
window.off('scroll', fixedHeader);
}
for (const el of $$('details[data-pref]')) {
el.open = compact ? false : prefs.get(el.dataset.pref);
}
}
function fixedHeader() {
const headerFixed = $('.fixed-header');
if (!headerFixed) headerHeight = $('#header').clientHeight;
const scrollPoint = headerHeight - 43;
if (window.scrollY >= scrollPoint && !headerFixed) {
$('body').style.setProperty('--fixed-padding', ` ${headerHeight}px`);
$('body').classList.add('fixed-header');
} else if (window.scrollY < scrollPoint && headerFixed) {
$('body').classList.remove('fixed-header');
}
}
});
//#endregion //#endregion
//#region init header //#region init header

View File

@ -1,5 +1,7 @@
:root { :root {
--fixed-padding: unset; --pad: 1rem; /* Edge padding for modals/blocks/whatnot. TODO: reuse it in more places */
--pad05: calc(0.5 * var(--pad));
--popup-button-width: 16px;
} }
body { body {
@ -70,7 +72,7 @@ html:not(.is-new-style) #heading::after {
#popup-button:hover { #popup-button:hover {
filter: drop-shadow(0 0 3px hsl(180, 70%, 50%)); filter: drop-shadow(0 0 3px hsl(180, 70%, 50%));
} }
.usercss body:not(.compact-layout) #popup-button { .usercss:not(.compact-layout) #popup-button {
right: 24px; right: 24px;
} }
/************ checkbox & select************/ /************ checkbox & select************/
@ -91,10 +93,10 @@ label {
#header { #header {
width: var(--header-width); width: var(--header-width);
height: 100vh; height: 100vh;
overflow: auto; overflow: hidden;
position: fixed; position: fixed;
top: 0; top: 0;
padding: 1rem; padding-top: var(--pad);
box-shadow: 0 0 3rem -1.2rem black; box-shadow: 0 0 3rem -1.2rem black;
box-sizing: border-box; box-sizing: border-box;
z-index: 10; z-index: 10;
@ -222,56 +224,61 @@ input:invalid {
margin-left: 0; margin-left: 0;
} }
/* collapsibles */ /* collapsibles */
#header details {
margin-right: var(--header-resizer-width);
}
#header details[open] {
overflow-y: auto;
margin-top: calc(1.5*var(--pad));
}
#header details[open] summary {
position: absolute;
margin-top: calc(-1.5*var(--pad));
}
#header summary { #header summary {
align-items: center; align-items: center;
margin-left: -13px; margin-left: .25em;
cursor: pointer; cursor: pointer;
} white-space: nowrap;
#header summary + * {
padding: .5rem 0;
} }
#header summary h2 { #header summary h2 {
display: inline-block; display: inline-block;
border-bottom: 1px dotted transparent; border-bottom: 1px dotted transparent;
margin-top: .1em; margin: 0 0 0 -13px;
margin-bottom: .1em;
margin-left: -13px;
padding-left: 13px; /* clicking directly on details-marker doesn't set pref so we cover it with h2 */ padding-left: 13px; /* clicking directly on details-marker doesn't set pref so we cover it with h2 */
max-width: calc(var(--header-width) - 2*var(--pad));
vertical-align: middle;
}
#header summary h2,
#header summary h2 > :first-child {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
#header summary:hover h2 { #header summary:hover h2 {
border-color: #bbb; border-color: #bbb;
} }
#header summary svg { #header summary svg {
margin-top: -3px; margin-top: -3px;
} }
#header details > :not(summary) {
margin: 0 0 0 var(--pad);
padding: calc(var(--pad)/4) 0;
}
#details-wrapper { #details-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 1; flex-grow: 1;
overflow: hidden;
margin-top: var(--pad05);
} }
#header details[open] + details[open] {
margin-top: .5rem;
}
#actions .buttons { #actions .buttons {
display: inline-flex; display: inline-flex;
flex-wrap: wrap; flex-wrap: wrap;
align-items: center; align-items: center;
} }
#actions .buttons > * { #actions .buttons > * {
margin: 0 .25rem .5rem 0; margin: 0 .25rem var(--pad05) 0;
}
#options:not([open]) + #lint h2 {
margin-top: 0;
}
#lint:not([open]) h2 {
margin-bottom: 0;
} }
#publish > div > * { #publish > div > * {
@ -373,11 +380,11 @@ input:invalid {
box-shadow: none !important; box-shadow: none !important;
pointer-events: auto !important; /* FF bug */ pointer-events: auto !important; /* FF bug */
} }
.section-editor .section { .sectioned .section {
margin: 0 0.7rem; margin: 0 0.7rem;
padding: 1rem; padding: 1rem;
} }
.section-editor .section:not(:first-child) { .sectioned .section:not(:first-child) {
border-top: 2px solid hsl(0, 0%, 80%); border-top: 2px solid hsl(0, 0%, 80%);
} }
.add-section:after { .add-section:after {
@ -413,10 +420,10 @@ input:invalid {
content: counter(codebox) ": " attr(data-text); content: counter(codebox) ": " attr(data-text);
margin-left: 0.25rem; margin-left: 0.25rem;
} }
.single-editor .applies-to { .usercss .applies-to {
border-width: 1px 0; border-width: 1px 0;
} }
.single-editor .applies-to > label::before { .usercss .applies-to > label::before {
content: attr(data-index) ":"; content: attr(data-index) ":";
margin-right: 0.25rem; margin-right: 0.25rem;
font-size: 12px; font-size: 12px;
@ -426,8 +433,6 @@ input:invalid {
} }
#toc { #toc {
counter-reset: codelabel; counter-reset: codelabel;
margin: 0;
padding: .5rem 0;
} }
#toc li { #toc li {
white-space: nowrap; white-space: nowrap;
@ -582,7 +587,7 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
padding: 0; padding: 0;
line-height: var(--input-height); line-height: var(--input-height);
} }
.section-editor .applies-to label { .sectioned .applies-to label {
margin-left: -24px; margin-left: -24px;
position: absolute; position: absolute;
} }
@ -829,42 +834,17 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
/************ lint ************/ /************ lint ************/
#lint { #lint {
overflow: hidden;
margin-left: -1rem;
margin-right: -1rem;
padding: 0;
box-sizing: border-box;
display: flex;
flex-grow: 1;
position: relative;
}
#lint > summary {
position: relative;
margin-left: 0;
padding-left: 4px;
}
#lint[open]:not(.hidden-unless-compact) {
min-height: 102px;
}
#lint summary h2 {
text-indent: -2px;
}
#lint > .lint-scroll-container {
margin: 1rem 10px 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
} }
#lint summary h2 {
display: inline-flex;
}
#lint table { #lint table {
font-size: 100%;
border-spacing: 0; border-spacing: 0;
margin-bottom: 1rem;
line-height: 1.0; line-height: 1.0;
width: 100%; width: 100%;
font-size: 85%;
cursor: pointer;
} }
#lint tr td:last-child { #lint tr td:last-child {
width: 100%; width: 100%;
@ -872,26 +852,28 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
#lint td[role="line"] { #lint td[role="line"] {
padding-left: 0.25rem; padding-left: 0.25rem;
} }
#lint table:last-child { #lint .report {
margin-bottom: 0; display: flex;
} }
#lint table.empty { #lint .empty {
display: none; display: none;
} }
#lint caption { #lint .caption {
text-align: left; vertical-align: top;
line-height: 16px;
font-weight: bold; font-weight: bold;
padding-bottom: 6px;
} }
#lint tbody { #lint .report:not(.empty) ~ :not(.empty) {
font-size: 85%; border-top: 1px dotted rgba(128, 128, 128, .5);
cursor: pointer; margin-top: .25em;
padding-top: .25em;
} }
#lint tr:hover { #lint tr:hover {
background-color: hsla(180, 50%, 36%, .2); background-color: hsla(180, 50%, 36%, .2);
} }
#lint td { #lint td {
padding: 0; padding: 0;
line-height: 16px;
} }
#lint td[role="severity"] { #lint td[role="severity"] {
font-size: 0; font-size: 0;
@ -908,6 +890,9 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
text-align: left; text-align: left;
white-space: nowrap; white-space: nowrap;
} }
#issue-count::before {
content: ':\A0';
}
#message-box.center.lint-config #message-box-contents { #message-box.center.lint-config #message-box-contents {
text-align: left; text-align: left;
} }
@ -965,16 +950,11 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
box-shadow: none; box-shadow: none;
} }
html:not(.usercss) .usercss-only, .sectioned .usercss-only,
.usercss .sectioned-only { .usercss .sectioned-only {
display: none !important; /* hide during page init */ display: none !important; /* hide during page init */
} }
body:not(.compact-layout) .hidden-unless-compact,
body.linter-disabled .hidden-unless-compact {
display: none !important;
}
#options:not([open]) + #lint { #options:not([open]) + #lint {
margin-top: 0; margin-top: 0;
} }
@ -1021,151 +1001,140 @@ body.linter-disabled .hidden-unless-compact {
margin-left: 0.2rem; margin-left: 0.2rem;
} }
/************ full-width only ************/
/* TODO: maybe move more rules here so we don't need to reset them in @media(max-width: 850px) */
@media (min-width: 851px) {
#header > :not(#details-wrapper):not(#header-resizer) {
margin-left: var(--pad);
margin-right: var(--pad);
}
#publish[open],
#header details:not([open]) {
flex: 0 0 auto;
}
#header details[open]:not(:last-child) {
margin-bottom: var(--pad05);
}
#header details:last-child {
padding-bottom: var(--pad);
}
}
/************ reponsive layouts ************/ /************ reponsive layouts ************/
@media(max-width: 850px) { @media(max-width: 850px) {
body { .sectioned body {
height: 100%;
}
.usercss body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.usercss #header,
#header:not(.sticky) {
position: static;
}
#header:not(.sticky) {
display: block;
}
#header { #header {
flex: 0 1 auto; flex: 0 0 auto;
height: unset; height: unset;
width: unset; width: 100%;
position: inherit; overflow: visible;
background: #fff;
border-right: none; border-right: none;
border-bottom: 1px dashed #AAA; border-bottom: 1px dashed #AAA;
padding: .5rem 1rem .5rem .5rem; padding: var(--pad05) var(--pad05) 0;
} }
.fixed-header { #header.sticky {
--fixed-height: 40px; flex-direction: row;
padding-top: var(--fixed-padding); box-shadow: 0 0 3rem -.75rem black;
}
.fixed-header #header {
min-height: var(--fixed-height);
position: fixed;
top: 0;
left: 0;
right: 0;
padding: 0;
background-color: #fff;
}
.fixed-header #header > *:not(#details-wrapper),
.fixed-header #options {
display: none !important;
}
.fixed-header #details-wrapper {
padding-top: calc((var(--fixed-height) - 1.2rem) / 2); /* 1.2 is the normal line height */
}
#header summary + *,
#lint > .lint-scroll-container {
margin-left: 1rem;
padding: .25rem 0 .5rem;
}
#header input[type="checkbox"] {
vertical-align: middle;
}
#header details {
margin: 0;
} }
#header.sticky #basic-info,
#header.sticky #mozilla-format-buttons,
#header.sticky .buttons > button,
#header.sticky .split-btn-pedal,
#heading, #heading,
h2 { h2 {
display: none; display: none;
} }
.popup-window #details-wrapper {
margin-right: var(--popup-button-width);
}
#basic-info { #basic-info {
margin-bottom: .5rem; margin: 0 var(--popup-button-width) var(--pad05) 0;
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
#basic-info #name,
#basic-info > *:first-child { #basic-info > *:first-child {
flex-grow: 1; flex-grow: 1;
} }
#basic-info > *:not(:last-child) { #basic-info > *:not(:last-child) {
margin-right: 0.8rem; margin-right: 0.8rem;
} }
#basic-info #name { #header details > :not(summary) {
flex-grow: 1; margin-left: var(--pad05);
} }
#options-wrapper { #header h2 {
display: flex; font-size: 14px;
flex-wrap: wrap; }
box-sizing: border-box; #actions {
display: inline-block;
} }
#details-wrapper { #details-wrapper {
display: inline-flex;
vertical-align: top;
max-width: 100%;
flex-direction: row; flex-direction: row;
flex-wrap: wrap; margin: .25em 0 var(--pad05);
} }
#options[open] { #details-wrapper details[open] {
width: 100%;
}
#sections-list[open] {
max-height: 102px;
}
#sections-list[open] #toc {
max-height: 60px;
overflow-y: auto;
}
#header details:not(#options) {
max-width: 50%;
}
.options-column {
flex-grow: 1;
padding-right: .5rem;
box-sizing: border-box;
}
.options-column > .usercss-only {
margin-bottom: 0;
}
#options-wrapper .options-column:nth-child(2) {
margin-top: 0; margin-top: 0;
z-index: 1000;
} }
#options:not([open]), #details-wrapper details[open]:hover,
#lint:not([open]) { #details-wrapper details[open]:focus-within {
overflow: initial; z-index: 1001;
} }
#options:not([open]) + #lint:not([open]) { #details-wrapper details[open] > summary {
margin-top: 0;
}
#lint summary {
position: static; position: static;
margin-bottom: 0; margin-top: 0;
} }
#header summary { #details-wrapper details[open] > summary + * {
margin-left: 0; position: absolute;
padding-left: 4px; overflow: hidden auto;
max-height: var(--max-height, 10vh);
background: #fff;
box-shadow: 0 6px 20px rgba(0, 0, 0, .3);
padding: var(--pad);
margin-top: var(--pad05);
} }
#header summary h2 { @media (max-height: 500px) {
margin: 0; #details-wrapper {
padding: 0; --max-height: 50px;
} }
.option label {
margin: 0;
} }
#options [type="number"] { #sections-list[open] > #toc {
text-align: left; /* workaround the column flow bug in webkit */ left: 0;
padding-left: 0.2rem; margin-left: auto;
margin-right: auto;
width: min-content;
} }
#options #tabSize-label { #sections-list[open] > #toc,
position: relative; #lint[open] > .lint-report-container {
top: 0.2rem; max-width: 50vw;
right: 0;
} }
#lint > .lint-scroll-container { #options[open],
padding-top: 0; #publish[open],
margin-right: 0; #lint:not([open]) {
} flex: 0 0 auto;
#lint { overflow-x: hidden;
padding: 0;
margin: .5rem 0 0;
}
#lint:not([open]) + #footer {
margin: .25em 0 -1em .25em;
} }
#sections { #sections {
height: unset !important;
padding-left: 0; padding-left: 0;
display: flex;
flex-direction: column;
flex: 1;
} }
#sections > :not(.single-editor) { #sections > :not(.single-editor) {
margin: 0 .5rem; margin: 0 .5rem;
@ -1175,10 +1144,6 @@ body.linter-disabled .hidden-unless-compact {
overflow: hidden; overflow: hidden;
flex: 1; flex: 1;
} }
.usercss #options:not([open]) ~ #lint.hidden ~ #footer,
.usercss #lint:not([open]) + #footer {
margin-top: -.25em;
}
#help-popup.big[style="display: block;"], #help-popup.big[style="display: block;"],
#help-popup[style="display: block;"] { #help-popup[style="display: block;"] {
width: max-content; width: max-content;

View File

@ -1,4 +1,4 @@
/* global $ $create messageBoxProxy waitForSheet */// dom.js /* global $$ $ $create messageBoxProxy waitForSheet */// dom.js
/* global API msg */// msg.js /* global API msg */// msg.js
/* global CodeMirror */ /* global CodeMirror */
/* global SectionsEditor */ /* global SectionsEditor */
@ -25,11 +25,7 @@ baseInit.ready.then(async () => {
await editor.ready; await editor.ready;
editor.ready = true; editor.ready = true;
editor.dirty.onChange(editor.updateDirty); editor.dirty.onChange(editor.updateDirty);
prefs.subscribe('editor.linter', () => linterMan.run());
prefs.subscribe('editor.linter', (key, value) => {
document.body.classList.toggle('linter-disabled', value === '');
linterMan.run();
});
// enabling after init to prevent flash of validation failure on an empty name // enabling after init to prevent flash of validation failure on an empty name
$('#name').required = !editor.isUsercss; $('#name').required = !editor.isUsercss;
@ -61,6 +57,42 @@ baseInit.ready.then(async () => {
'/edit/drafts', '/edit/drafts',
'/edit/global-search', '/edit/global-search',
]); ]);
}).then(() => {
// Set up mini-header on scroll
const {isUsercss} = editor;
const el = $create({
style: `
top: 0;
height: 1px;
position: absolute;
visibility: hidden;
`.replace(/;/g, '!important;'),
});
const scroller = isUsercss ? $('.CodeMirror-scroll') : document.body;
const xoRoot = isUsercss ? scroller : undefined;
const xo = new IntersectionObserver(onScrolled, {root: xoRoot});
scroller.appendChild(el);
onCompactToggled(baseInit.mqCompact);
baseInit.mqCompact.on('change', onCompactToggled);
/** @param {MediaQueryList} mq */
function onCompactToggled(mq) {
for (const el of $$('details[data-pref]')) {
el.open = mq.matches ? false : prefs.get(el.dataset.pref);
}
if (mq.matches) {
xo.observe(el);
} else {
xo.disconnect();
}
}
/** @param {IntersectionObserverEntry[]} entries */
function onScrolled(entries) {
const h = $('#header');
const sticky = !entries.pop().isIntersecting;
if (!isUsercss) scroller.style.paddingTop = sticky ? h.offsetHeight + 'px' : '';
h.classList.toggle('sticky', sticky);
}
}); });
//#endregion //#endregion

View File

@ -20,7 +20,8 @@
title: t('optionsCustomizePopup') + '\n' + POPUP_HOTKEY, title: t('optionsCustomizePopup') + '\n' + POPUP_HOTKEY,
onclick: embedPopup, onclick: embedPopup,
}); });
document.documentElement.appendChild(btn); $.root.appendChild(btn);
$.rootCL.add('popup-window');
baseInit.domReady.then(() => { baseInit.domReady.then(() => {
document.body.appendChild(btn); document.body.appendChild(btn);
// Adding a dummy command to show in keymap help popup // Adding a dummy command to show in keymap help popup

View File

@ -54,7 +54,7 @@
undoHistory: [], undoHistory: [],
searchInApplies: !document.documentElement.classList.contains('usercss'), searchInApplies: !editor.isUsercss,
}; };
//endregion //endregion
@ -588,7 +588,7 @@
input: colorMimicry($('input:not(:disabled)'), {bg: 'backgroundColor'}), input: colorMimicry($('input:not(:disabled)'), {bg: 'backgroundColor'}),
icon: colorMimicry($$('svg.info')[1], {fill: 'fill'}), icon: colorMimicry($$('svg.info')[1], {fill: 'fill'}),
}; };
document.documentElement.appendChild( $.root.appendChild(
$(DIALOG_STYLE_SELECTOR) || $(DIALOG_STYLE_SELECTOR) ||
$create('style' + DIALOG_STYLE_SELECTOR) $create('style' + DIALOG_STYLE_SELECTOR)
).textContent = ` ).textContent = `

View File

@ -312,7 +312,7 @@ linterMan.DEFAULTS = {
function updateCount() { function updateCount() {
const issueCount = Array.from(tables.values()) const issueCount = Array.from(tables.values())
.reduce((sum, table) => sum + table.trs.length, 0); .reduce((sum, table) => sum + table.trs.length, 0);
$('#lint').classList.toggle('hidden-unless-compact', issueCount === 0); $('#lint').hidden = !issueCount;
$('#issue-count').textContent = issueCount; $('#issue-count').textContent = issueCount;
} }
@ -328,19 +328,20 @@ linterMan.DEFAULTS = {
} }
function createTable(cm) { function createTable(cm) {
const caption = $create('caption'); const caption = $create('.caption');
const tbody = $create('tbody'); const table = $create('table');
const table = $create('table', [caption, tbody]); const report = $create('.report', [caption, table]);
const trs = []; const trs = [];
return { return {
element: table, element: report,
trs, trs,
updateAnnotations, updateAnnotations,
updateCaption, updateCaption,
}; };
function updateCaption() { function updateCaption() {
caption.textContent = editor.getEditorTitle(cm); const t = editor.getEditorTitle(cm);
Object.assign(caption, typeof t == 'string' ? {textContent: t} : t);
} }
function updateAnnotations(lines) { function updateAnnotations(lines) {
@ -352,20 +353,20 @@ linterMan.DEFAULTS = {
} else { } else {
tr = createTr(); tr = createTr();
trs.push(tr); trs.push(tr);
tbody.append(tr.element); table.appendChild(tr.element);
} }
tr.update(anno); tr.update(anno);
i++; i++;
} }
if (i === 0) { if (i === 0) {
trs.length = 0; trs.length = 0;
tbody.textContent = ''; table.textContent = '';
} else { } else {
while (trs.length > i) { while (trs.length > i) {
trs.pop().element.remove(); trs.pop().element.remove();
} }
} }
table.classList.toggle('empty', trs.length === 0); report.classList.toggle('empty', !trs.length);
function *getAnnotations() { function *getAnnotations() {
for (const line of lines.filter(Boolean)) { for (const line of lines.filter(Boolean)) {

View File

@ -218,7 +218,7 @@ function MozSectionWidget(cm, finder = MozSectionFinder(cm)) {
transition: none; transition: none;
} }
`; `;
document.documentElement.appendChild(actualStyle); $.root.appendChild(actualStyle);
} }
/** /**

View File

@ -25,7 +25,6 @@ function SectionsEditor() {
updateMeta(); updateMeta();
rerouteHotkeys.toggle(true); // enabled initially because we don't always focus a CodeMirror rerouteHotkeys.toggle(true); // enabled initially because we don't always focus a CodeMirror
editor.livePreview.init(); editor.livePreview.init();
container.classList.add('section-editor');
$('#to-mozilla').on('click', showMozillaFormat); $('#to-mozilla').on('click', showMozillaFormat);
$('#to-mozilla-help').on('click', showToMozillaHelp); $('#to-mozilla-help').on('click', showToMozillaHelp);
$('#from-mozilla').on('click', () => showMozillaFormatImport()); $('#from-mozilla').on('click', () => showMozillaFormatImport());
@ -46,8 +45,11 @@ function SectionsEditor() {
}, },
getEditorTitle(cm) { getEditorTitle(cm) {
const index = editor.getEditors().indexOf(cm); const index = editor.getEditors().indexOf(cm) + 1;
return `${t('sectionCode')} ${index + 1}`; return {
textContent: `#${index}`,
title: `${t('sectionCode')} ${index}`,
};
}, },
getValue(asObject) { getValue(asObject) {
@ -394,7 +396,7 @@ function SectionsEditor() {
} }
function lockPageUI(locked) { function lockPageUI(locked) {
document.documentElement.style.pointerEvents = locked ? 'none' : ''; $.root.style.pointerEvents = locked ? 'none' : '';
if (popup.codebox) { if (popup.codebox) {
popup.classList.toggle('ready', locked ? false : !popup.codebox.isBlank()); popup.classList.toggle('ready', locked ? false : !popup.codebox.isBlank());
popup.codebox.options.readOnly = locked; popup.codebox.options.readOnly = locked;

View File

@ -195,7 +195,7 @@ function showCodeMirrorPopup(title, html, options) {
}, options)); }, options));
cm.focus(); cm.focus();
document.documentElement.style.pointerEvents = 'none'; $.root.style.pointerEvents = 'none';
popup.style.pointerEvents = 'auto'; popup.style.pointerEvents = 'auto';
const onKeyDown = event => { const onKeyDown = event => {
@ -210,7 +210,7 @@ function showCodeMirrorPopup(title, html, options) {
window.on('closeHelp', () => { window.on('closeHelp', () => {
window.off('keydown', onKeyDown, true); window.off('keydown', onKeyDown, true);
document.documentElement.style.removeProperty('pointer-events'); $.root.style.removeProperty('pointer-events');
cm = popup.codebox = null; cm = popup.codebox = null;
}, {once: true}); }, {once: true});

View File

@ -277,13 +277,15 @@ input[type="number"][data-focused-via-click]:focus {
/* header resizer */ /* header resizer */
:root { :root {
--header-width: 280px; --header-width: 280px;
--header-resizer-width: 8px;
} }
#header-resizer { #header-resizer {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
width: 6px; width: var(--header-resizer-width);
box-sizing: border-box;
cursor: e-resize; cursor: e-resize;
border-width: 0 1px; border-width: 0 1px;
border-style: solid; border-style: solid;
@ -318,6 +320,7 @@ body.resizing-v > * {
.split-btn { .split-btn {
position: relative; position: relative;
white-space: nowrap;
} }
.split-btn-pedal { .split-btn-pedal {
margin-left: -1px !important; margin-left: -1px !important;

View File

@ -27,6 +27,9 @@ Object.assign(EventTarget.prototype, {
//#region Exports //#region Exports
$.root = document.documentElement;
$.rootCL = $.root.classList;
// Makes the focus outline appear on keyboard tabbing, but not on mouse clicks. // Makes the focus outline appear on keyboard tabbing, but not on mouse clicks.
const focusAccessibility = { const focusAccessibility = {
// last event's focusedViaClick // last event's focusedViaClick
@ -292,8 +295,8 @@ function scrollElementIntoView(element, {invalidMarginRatio = 0} = {}) {
const windowHeight = window.innerHeight; const windowHeight = window.innerHeight;
if (top < Math.max(parentTop, windowHeight * invalidMarginRatio) || if (top < Math.max(parentTop, windowHeight * invalidMarginRatio) ||
top > Math.min(parentBottom, windowHeight) - height - windowHeight * invalidMarginRatio) { top > Math.min(parentBottom, windowHeight) - height - windowHeight * invalidMarginRatio) {
const scroller = element.closest('.scroller'); const scroller = element.closest('.scroller') || window;
scroller.scrollBy(0, top - (scroller ? scroller.clientHeight : windowHeight) / 2 + height); scroller.scrollBy(0, top - (scroller.clientHeight || windowHeight) / 2 + height);
} }
} }
@ -474,10 +477,9 @@ const dom = {};
const lazyScripts = [ const lazyScripts = [
'/js/dom-on-load', '/js/dom-on-load',
]; ];
const elHtml = document.documentElement; if (!UA.windows) $.rootCL.add('non-windows');
if (!UA.windows) elHtml.classList.add('non-windows');
// set language for a) CSS :lang pseudo and b) hyphenation // set language for a) CSS :lang pseudo and b) hyphenation
elHtml.setAttribute('lang', chrome.i18n.getUILanguage()); $.root.setAttribute('lang', chrome.i18n.getUILanguage());
// set up header width resizer // set up header width resizer
const HW = 'headerWidth.'; const HW = 'headerWidth.';
const HWprefId = HW + location.pathname.match(/^.(\w*)/)[1]; const HWprefId = HW + location.pathname.match(/^.(\w*)/)[1];
@ -489,7 +491,7 @@ const dom = {};
// If this is a small window on a big monitor the user can maximize it later // If this is a small window on a big monitor the user can maximize it later
const max = (innerWidth < 850 ? screen.availWidth : innerWidth) / 3; const max = (innerWidth < 850 ? screen.availWidth : innerWidth) / 3;
width = Math.round(Math.max(200, Math.min(max, Number(width) || 0))); width = Math.round(Math.max(200, Math.min(max, Number(width) || 0)));
elHtml.style.setProperty('--header-width', width + 'px'); $.root.style.setProperty('--header-width', width + 'px');
return width; return width;
}, },
}); });

View File

@ -36,9 +36,8 @@ Object.assign(newUI, {
ids: Object.keys(newUI), ids: Object.keys(newUI),
prefKeyForId: id => `manage.newUI.${id}`.replace(/\.enabled$/, ''), prefKeyForId: id => `manage.newUI.${id}`.replace(/\.enabled$/, ''),
renderClass: () => { renderClass: () => {
const cl = document.documentElement.classList; $.rootCL.toggle('newUI', newUI.enabled);
cl.toggle('newUI', newUI.enabled); $.rootCL.toggle('oldUI', !newUI.enabled);
cl.toggle('oldUI', !newUI.enabled);
}, },
}); });
// ...read the actual values // ...read the actual values
@ -128,7 +127,7 @@ function onRuntimeMessage(msg) {
async function toggleEmbeddedOptions(state) { async function toggleEmbeddedOptions(state) {
const el = $('#stylus-embedded-options') || const el = $('#stylus-embedded-options') ||
state && document.documentElement.appendChild($create('iframe', { state && $.root.appendChild($create('iframe', {
id: 'stylus-embedded-options', id: 'stylus-embedded-options',
src: '/options.html', src: '/options.html',
})); }));

View File

@ -172,11 +172,11 @@ const sorter = (() => {
function updateColumnCount() { function updateColumnCount() {
let newValue = 1; let newValue = 1;
for (let el = document.documentElement.lastElementChild; for (let el = $.root.lastElementChild;
el.localName === 'style'; el.localName === 'style';
el = el.previousElementSibling) { el = el.previousElementSibling) {
if (el.textContent.includes('--columns:')) { if (el.textContent.includes('--columns:')) {
newValue = Math.max(1, getComputedStyle(document.documentElement).getPropertyValue('--columns') | 0); newValue = Math.max(1, getComputedStyle($.root).getPropertyValue('--columns') | 0);
break; break;
} }
} }

View File

@ -38,7 +38,7 @@ preinit.then(({frames, styles, url}) => {
msg.onExtension(onRuntimeMessage); msg.onExtension(onRuntimeMessage);
prefs.subscribe('popup.stylesFirst', (key, stylesFirst) => { prefs.subscribe('popup.stylesFirst', (key, stylesFirst) => {
document.documentElement.classList.toggle('styles-last', !stylesFirst); $.rootCL.toggle('styles-last', !stylesFirst);
}, {runNow: true}); }, {runNow: true});
if (CHROME_POPUP_BORDER_BUG) { if (CHROME_POPUP_BORDER_BUG) {
prefs.subscribe('popup.borders', toggleSideBorders, {runNow: true}); prefs.subscribe('popup.borders', toggleSideBorders, {runNow: true});
@ -70,7 +70,7 @@ function setPopupWidth(_key, width) {
function toggleSideBorders(_key, state) { function toggleSideBorders(_key, state) {
// runs before <body> is parsed // runs before <body> is parsed
const style = document.documentElement.style; const style = $.root.style;
if (state) { if (state) {
style.cssText += style.cssText +=
'border-left: 2px solid white !important;' + 'border-left: 2px solid white !important;' +