From dd38856eda796683c6197a8c41710e1ff4f44420 Mon Sep 17 00:00:00 2001 From: tophf Date: Mon, 14 Feb 2022 22:19:20 +0300 Subject: [PATCH] scrollable details + sticky header (#1400) * shorten section labels in lint report * `sectioned` class on html for sectioned editor * fix scrollElementIntoView --- edit.html | 8 +- edit/base.js | 57 +------ edit/edit.css | 337 +++++++++++++++++-------------------- edit/edit.js | 44 ++++- edit/embedded-popup.js | 3 +- edit/global-search.js | 4 +- edit/linter-manager.js | 19 ++- edit/moz-section-widget.js | 2 +- edit/sections-editor.js | 10 +- edit/util.js | 4 +- global.css | 5 +- js/dom.js | 14 +- manage/manage.js | 7 +- manage/sorter.js | 4 +- popup/popup.js | 4 +- 15 files changed, 242 insertions(+), 280 deletions(-) diff --git a/edit.html b/edit.html index 05936741..8dd7cf5e 100644 --- a/edit.html +++ b/edit.html @@ -418,17 +418,15 @@

    -
    +
    diff --git a/edit/base.js b/edit/base.js index a42b5f65..c9cca9e3 100644 --- a/edit/base.js +++ b/edit/base.js @@ -6,14 +6,7 @@ /* global initBeautifyButton */// beautify.js /* global prefs */ /* global t */// localization.js -/* global - FIREFOX - debounce - getOwnTab - sessionStore - tryJSONparse - tryURL -*/// toolbox.js +/* global FIREFOX getOwnTab sessionStore tryJSONparse tryURL */// toolbox.js 'use strict'; /** @@ -39,7 +32,7 @@ const editor = { cancel: () => location.assign('/manage.html'), updateClass() { - document.documentElement.classList.toggle('is-new-style', !editor.style.id); + $.rootCL.toggle('is-new-style', !editor.style.id); }, updateTitle(isDirty = editor.dirty.isDirty()) { @@ -56,9 +49,14 @@ const editor = { const baseInit = (() => { 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 { domReady, + mqCompact, ready: Promise.all([ domReady, loadStyle(), @@ -94,7 +92,7 @@ const baseInit = (() => { editor.style = style; editor.updateClass(); editor.updateTitle(false); - document.documentElement.classList.toggle('usercss', editor.isUsercss); + $.rootCL.add(editor.isUsercss ? 'usercss' : 'sectioned'); sessionStore.justEditedStyleId = style.id || ''; // no such style so let's clear the invalid URL parameters 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 //#region init header diff --git a/edit/edit.css b/edit/edit.css index 2bad83a1..b8acdf96 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -1,5 +1,7 @@ :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 { @@ -70,7 +72,7 @@ html:not(.is-new-style) #heading::after { #popup-button:hover { 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; } /************ checkbox & select************/ @@ -91,10 +93,10 @@ label { #header { width: var(--header-width); height: 100vh; - overflow: auto; + overflow: hidden; position: fixed; top: 0; - padding: 1rem; + padding-top: var(--pad); box-shadow: 0 0 3rem -1.2rem black; box-sizing: border-box; z-index: 10; @@ -222,56 +224,61 @@ input:invalid { margin-left: 0; } /* 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 { align-items: center; - margin-left: -13px; + margin-left: .25em; cursor: pointer; -} -#header summary + * { - padding: .5rem 0; + white-space: nowrap; } #header summary h2 { display: inline-block; border-bottom: 1px dotted transparent; - margin-top: .1em; - margin-bottom: .1em; - margin-left: -13px; + margin: 0 0 0 -13px; 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 { border-color: #bbb; } - #header summary svg { margin-top: -3px; } - +#header details > :not(summary) { + margin: 0 0 0 var(--pad); + padding: calc(var(--pad)/4) 0; +} #details-wrapper { display: flex; flex-direction: column; flex-grow: 1; + overflow: hidden; + margin-top: var(--pad05); } - -#header details[open] + details[open] { - margin-top: .5rem; -} - #actions .buttons { display: inline-flex; flex-wrap: wrap; align-items: center; } - #actions .buttons > * { - margin: 0 .25rem .5rem 0; -} - -#options:not([open]) + #lint h2 { - margin-top: 0; -} -#lint:not([open]) h2 { - margin-bottom: 0; + margin: 0 .25rem var(--pad05) 0; } #publish > div > * { @@ -373,11 +380,11 @@ input:invalid { box-shadow: none !important; pointer-events: auto !important; /* FF bug */ } -.section-editor .section { +.sectioned .section { margin: 0 0.7rem; padding: 1rem; } -.section-editor .section:not(:first-child) { +.sectioned .section:not(:first-child) { border-top: 2px solid hsl(0, 0%, 80%); } .add-section:after { @@ -413,10 +420,10 @@ input:invalid { content: counter(codebox) ": " attr(data-text); margin-left: 0.25rem; } -.single-editor .applies-to { +.usercss .applies-to { border-width: 1px 0; } -.single-editor .applies-to > label::before { +.usercss .applies-to > label::before { content: attr(data-index) ":"; margin-right: 0.25rem; font-size: 12px; @@ -426,8 +433,6 @@ input:invalid { } #toc { counter-reset: codelabel; - margin: 0; - padding: .5rem 0; } #toc li { white-space: nowrap; @@ -582,7 +587,7 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high padding: 0; line-height: var(--input-height); } -.section-editor .applies-to label { +.sectioned .applies-to label { margin-left: -24px; position: absolute; } @@ -829,42 +834,17 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high /************ 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; } +#lint summary h2 { + display: inline-flex; +} #lint table { - font-size: 100%; border-spacing: 0; - margin-bottom: 1rem; line-height: 1.0; width: 100%; + font-size: 85%; + cursor: pointer; } #lint tr td:last-child { width: 100%; @@ -872,26 +852,28 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high #lint td[role="line"] { padding-left: 0.25rem; } -#lint table:last-child { - margin-bottom: 0; +#lint .report { + display: flex; } -#lint table.empty { +#lint .empty { display: none; } -#lint caption { - text-align: left; +#lint .caption { + vertical-align: top; + line-height: 16px; font-weight: bold; - padding-bottom: 6px; } -#lint tbody { - font-size: 85%; - cursor: pointer; +#lint .report:not(.empty) ~ :not(.empty) { + border-top: 1px dotted rgba(128, 128, 128, .5); + margin-top: .25em; + padding-top: .25em; } #lint tr:hover { background-color: hsla(180, 50%, 36%, .2); } #lint td { padding: 0; + line-height: 16px; } #lint td[role="severity"] { font-size: 0; @@ -908,6 +890,9 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high text-align: left; white-space: nowrap; } +#issue-count::before { + content: ':\A0'; +} #message-box.center.lint-config #message-box-contents { text-align: left; } @@ -965,16 +950,11 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high box-shadow: none; } -html:not(.usercss) .usercss-only, +.sectioned .usercss-only, .usercss .sectioned-only { 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 { margin-top: 0; } @@ -1021,151 +1001,140 @@ body.linter-disabled .hidden-unless-compact { 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 ************/ @media(max-width: 850px) { - body { + .sectioned body { + height: 100%; + } + .usercss body { display: flex; flex-direction: column; } + .usercss #header, + #header:not(.sticky) { + position: static; + } + #header:not(.sticky) { + display: block; + } #header { - flex: 0 1 auto; + flex: 0 0 auto; height: unset; - width: unset; - position: inherit; + width: 100%; + overflow: visible; + background: #fff; border-right: none; border-bottom: 1px dashed #AAA; - padding: .5rem 1rem .5rem .5rem; + padding: var(--pad05) var(--pad05) 0; } - .fixed-header { - --fixed-height: 40px; - padding-top: var(--fixed-padding); - } - .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 { + flex-direction: row; + box-shadow: 0 0 3rem -.75rem black; } + #header.sticky #basic-info, + #header.sticky #mozilla-format-buttons, + #header.sticky .buttons > button, + #header.sticky .split-btn-pedal, #heading, h2 { display: none; } + .popup-window #details-wrapper { + margin-right: var(--popup-button-width); + } #basic-info { - margin-bottom: .5rem; + margin: 0 var(--popup-button-width) var(--pad05) 0; box-sizing: border-box; display: flex; flex-wrap: wrap; } + #basic-info #name, #basic-info > *:first-child { flex-grow: 1; } #basic-info > *:not(:last-child) { margin-right: 0.8rem; } - #basic-info #name { - flex-grow: 1; + #header details > :not(summary) { + margin-left: var(--pad05); } - #options-wrapper { - display: flex; - flex-wrap: wrap; - box-sizing: border-box; + #header h2 { + font-size: 14px; + } + #actions { + display: inline-block; } #details-wrapper { + display: inline-flex; + vertical-align: top; + max-width: 100%; flex-direction: row; - flex-wrap: wrap; + margin: .25em 0 var(--pad05); } - #options[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) { + #details-wrapper details[open] { margin-top: 0; + z-index: 1000; } - #options:not([open]), - #lint:not([open]) { - overflow: initial; + #details-wrapper details[open]:hover, + #details-wrapper details[open]:focus-within { + z-index: 1001; } - #options:not([open]) + #lint:not([open]) { - margin-top: 0; - } - #lint summary { + #details-wrapper details[open] > summary { position: static; - margin-bottom: 0; + margin-top: 0; } - #header summary { - margin-left: 0; - padding-left: 4px; + #details-wrapper details[open] > summary + * { + position: absolute; + 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 { - margin: 0; - padding: 0; + @media (max-height: 500px) { + #details-wrapper { + --max-height: 50px; + } } - .option label { - margin: 0; + #sections-list[open] > #toc { + left: 0; + margin-left: auto; + margin-right: auto; + width: min-content; } - #options [type="number"] { - text-align: left; /* workaround the column flow bug in webkit */ - padding-left: 0.2rem; + #sections-list[open] > #toc, + #lint[open] > .lint-report-container { + max-width: 50vw; + right: 0; } - #options #tabSize-label { - position: relative; - top: 0.2rem; - } - #lint > .lint-scroll-container { - padding-top: 0; - margin-right: 0; - } - #lint { - padding: 0; - margin: .5rem 0 0; - } - #lint:not([open]) + #footer { - margin: .25em 0 -1em .25em; + #options[open], + #publish[open], + #lint:not([open]) { + flex: 0 0 auto; + overflow-x: hidden; } #sections { - height: unset !important; padding-left: 0; - display: flex; - flex-direction: column; - flex: 1; } #sections > :not(.single-editor) { margin: 0 .5rem; @@ -1175,10 +1144,6 @@ body.linter-disabled .hidden-unless-compact { overflow: hidden; 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[style="display: block;"] { width: max-content; diff --git a/edit/edit.js b/edit/edit.js index 64e5c0ea..910ea6d1 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1,4 +1,4 @@ -/* global $ $create messageBoxProxy waitForSheet */// dom.js +/* global $$ $ $create messageBoxProxy waitForSheet */// dom.js /* global API msg */// msg.js /* global CodeMirror */ /* global SectionsEditor */ @@ -25,11 +25,7 @@ baseInit.ready.then(async () => { await editor.ready; editor.ready = true; editor.dirty.onChange(editor.updateDirty); - - prefs.subscribe('editor.linter', (key, value) => { - document.body.classList.toggle('linter-disabled', value === ''); - linterMan.run(); - }); + prefs.subscribe('editor.linter', () => linterMan.run()); // enabling after init to prevent flash of validation failure on an empty name $('#name').required = !editor.isUsercss; @@ -61,6 +57,42 @@ baseInit.ready.then(async () => { '/edit/drafts', '/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 diff --git a/edit/embedded-popup.js b/edit/embedded-popup.js index a503bc6f..b09529c7 100644 --- a/edit/embedded-popup.js +++ b/edit/embedded-popup.js @@ -20,7 +20,8 @@ title: t('optionsCustomizePopup') + '\n' + POPUP_HOTKEY, onclick: embedPopup, }); - document.documentElement.appendChild(btn); + $.root.appendChild(btn); + $.rootCL.add('popup-window'); baseInit.domReady.then(() => { document.body.appendChild(btn); // Adding a dummy command to show in keymap help popup diff --git a/edit/global-search.js b/edit/global-search.js index 664409fd..e4ebbaa8 100644 --- a/edit/global-search.js +++ b/edit/global-search.js @@ -54,7 +54,7 @@ undoHistory: [], - searchInApplies: !document.documentElement.classList.contains('usercss'), + searchInApplies: !editor.isUsercss, }; //endregion @@ -588,7 +588,7 @@ input: colorMimicry($('input:not(:disabled)'), {bg: 'backgroundColor'}), icon: colorMimicry($$('svg.info')[1], {fill: 'fill'}), }; - document.documentElement.appendChild( + $.root.appendChild( $(DIALOG_STYLE_SELECTOR) || $create('style' + DIALOG_STYLE_SELECTOR) ).textContent = ` diff --git a/edit/linter-manager.js b/edit/linter-manager.js index a4c26751..1a4fd269 100644 --- a/edit/linter-manager.js +++ b/edit/linter-manager.js @@ -312,7 +312,7 @@ linterMan.DEFAULTS = { function updateCount() { const issueCount = Array.from(tables.values()) .reduce((sum, table) => sum + table.trs.length, 0); - $('#lint').classList.toggle('hidden-unless-compact', issueCount === 0); + $('#lint').hidden = !issueCount; $('#issue-count').textContent = issueCount; } @@ -328,19 +328,20 @@ linterMan.DEFAULTS = { } function createTable(cm) { - const caption = $create('caption'); - const tbody = $create('tbody'); - const table = $create('table', [caption, tbody]); + const caption = $create('.caption'); + const table = $create('table'); + const report = $create('.report', [caption, table]); const trs = []; return { - element: table, + element: report, trs, updateAnnotations, 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) { @@ -352,20 +353,20 @@ linterMan.DEFAULTS = { } else { tr = createTr(); trs.push(tr); - tbody.append(tr.element); + table.appendChild(tr.element); } tr.update(anno); i++; } if (i === 0) { trs.length = 0; - tbody.textContent = ''; + table.textContent = ''; } else { while (trs.length > i) { trs.pop().element.remove(); } } - table.classList.toggle('empty', trs.length === 0); + report.classList.toggle('empty', !trs.length); function *getAnnotations() { for (const line of lines.filter(Boolean)) { diff --git a/edit/moz-section-widget.js b/edit/moz-section-widget.js index 92a0bfa9..674d03b1 100644 --- a/edit/moz-section-widget.js +++ b/edit/moz-section-widget.js @@ -218,7 +218,7 @@ function MozSectionWidget(cm, finder = MozSectionFinder(cm)) { transition: none; } `; - document.documentElement.appendChild(actualStyle); + $.root.appendChild(actualStyle); } /** diff --git a/edit/sections-editor.js b/edit/sections-editor.js index edcf569c..4154b02a 100644 --- a/edit/sections-editor.js +++ b/edit/sections-editor.js @@ -25,7 +25,6 @@ function SectionsEditor() { updateMeta(); rerouteHotkeys.toggle(true); // enabled initially because we don't always focus a CodeMirror editor.livePreview.init(); - container.classList.add('section-editor'); $('#to-mozilla').on('click', showMozillaFormat); $('#to-mozilla-help').on('click', showToMozillaHelp); $('#from-mozilla').on('click', () => showMozillaFormatImport()); @@ -46,8 +45,11 @@ function SectionsEditor() { }, getEditorTitle(cm) { - const index = editor.getEditors().indexOf(cm); - return `${t('sectionCode')} ${index + 1}`; + const index = editor.getEditors().indexOf(cm) + 1; + return { + textContent: `#${index}`, + title: `${t('sectionCode')} ${index}`, + }; }, getValue(asObject) { @@ -394,7 +396,7 @@ function SectionsEditor() { } function lockPageUI(locked) { - document.documentElement.style.pointerEvents = locked ? 'none' : ''; + $.root.style.pointerEvents = locked ? 'none' : ''; if (popup.codebox) { popup.classList.toggle('ready', locked ? false : !popup.codebox.isBlank()); popup.codebox.options.readOnly = locked; diff --git a/edit/util.js b/edit/util.js index ddc5f30f..9a7f2c3d 100644 --- a/edit/util.js +++ b/edit/util.js @@ -195,7 +195,7 @@ function showCodeMirrorPopup(title, html, options) { }, options)); cm.focus(); - document.documentElement.style.pointerEvents = 'none'; + $.root.style.pointerEvents = 'none'; popup.style.pointerEvents = 'auto'; const onKeyDown = event => { @@ -210,7 +210,7 @@ function showCodeMirrorPopup(title, html, options) { window.on('closeHelp', () => { window.off('keydown', onKeyDown, true); - document.documentElement.style.removeProperty('pointer-events'); + $.root.style.removeProperty('pointer-events'); cm = popup.codebox = null; }, {once: true}); diff --git a/global.css b/global.css index cbb92c9e..3d80529a 100644 --- a/global.css +++ b/global.css @@ -277,13 +277,15 @@ input[type="number"][data-focused-via-click]:focus { /* header resizer */ :root { --header-width: 280px; + --header-resizer-width: 8px; } #header-resizer { position: absolute; top: 0; right: 0; bottom: 0; - width: 6px; + width: var(--header-resizer-width); + box-sizing: border-box; cursor: e-resize; border-width: 0 1px; border-style: solid; @@ -318,6 +320,7 @@ body.resizing-v > * { .split-btn { position: relative; + white-space: nowrap; } .split-btn-pedal { margin-left: -1px !important; diff --git a/js/dom.js b/js/dom.js index 241c8b12..f89fcf8a 100644 --- a/js/dom.js +++ b/js/dom.js @@ -27,6 +27,9 @@ Object.assign(EventTarget.prototype, { //#region Exports +$.root = document.documentElement; +$.rootCL = $.root.classList; + // Makes the focus outline appear on keyboard tabbing, but not on mouse clicks. const focusAccessibility = { // last event's focusedViaClick @@ -292,8 +295,8 @@ function scrollElementIntoView(element, {invalidMarginRatio = 0} = {}) { const windowHeight = window.innerHeight; if (top < Math.max(parentTop, windowHeight * invalidMarginRatio) || top > Math.min(parentBottom, windowHeight) - height - windowHeight * invalidMarginRatio) { - const scroller = element.closest('.scroller'); - scroller.scrollBy(0, top - (scroller ? scroller.clientHeight : windowHeight) / 2 + height); + const scroller = element.closest('.scroller') || window; + scroller.scrollBy(0, top - (scroller.clientHeight || windowHeight) / 2 + height); } } @@ -474,10 +477,9 @@ const dom = {}; const lazyScripts = [ '/js/dom-on-load', ]; - const elHtml = document.documentElement; - if (!UA.windows) elHtml.classList.add('non-windows'); + if (!UA.windows) $.rootCL.add('non-windows'); // 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 const HW = 'headerWidth.'; 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 const max = (innerWidth < 850 ? screen.availWidth : innerWidth) / 3; 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; }, }); diff --git a/manage/manage.js b/manage/manage.js index 96f91ffb..31d5a4a7 100644 --- a/manage/manage.js +++ b/manage/manage.js @@ -36,9 +36,8 @@ Object.assign(newUI, { ids: Object.keys(newUI), prefKeyForId: id => `manage.newUI.${id}`.replace(/\.enabled$/, ''), renderClass: () => { - const cl = document.documentElement.classList; - cl.toggle('newUI', newUI.enabled); - cl.toggle('oldUI', !newUI.enabled); + $.rootCL.toggle('newUI', newUI.enabled); + $.rootCL.toggle('oldUI', !newUI.enabled); }, }); // ...read the actual values @@ -128,7 +127,7 @@ function onRuntimeMessage(msg) { async function toggleEmbeddedOptions(state) { const el = $('#stylus-embedded-options') || - state && document.documentElement.appendChild($create('iframe', { + state && $.root.appendChild($create('iframe', { id: 'stylus-embedded-options', src: '/options.html', })); diff --git a/manage/sorter.js b/manage/sorter.js index 97963e54..7380bfab 100644 --- a/manage/sorter.js +++ b/manage/sorter.js @@ -172,11 +172,11 @@ const sorter = (() => { function updateColumnCount() { let newValue = 1; - for (let el = document.documentElement.lastElementChild; + for (let el = $.root.lastElementChild; el.localName === 'style'; el = el.previousElementSibling) { 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; } } diff --git a/popup/popup.js b/popup/popup.js index acd3d5f9..7e7dd932 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -38,7 +38,7 @@ preinit.then(({frames, styles, url}) => { msg.onExtension(onRuntimeMessage); prefs.subscribe('popup.stylesFirst', (key, stylesFirst) => { - document.documentElement.classList.toggle('styles-last', !stylesFirst); + $.rootCL.toggle('styles-last', !stylesFirst); }, {runNow: true}); if (CHROME_POPUP_BORDER_BUG) { prefs.subscribe('popup.borders', toggleSideBorders, {runNow: true}); @@ -70,7 +70,7 @@ function setPopupWidth(_key, width) { function toggleSideBorders(_key, state) { // runs before is parsed - const style = document.documentElement.style; + const style = $.root.style; if (state) { style.cssText += 'border-left: 2px solid white !important;' +