set classes dynamically

* `sticky` on #header when scrolled
* `sectioned` on html for sectioned editor
This commit is contained in:
tophf 2022-02-13 07:56:55 +03:00
parent aad4828e4a
commit d25ef97205
4 changed files with 65 additions and 31 deletions

View File

@ -1,4 +1,4 @@
/* global $ $$ $create setupLivePrefs waitForSelector */// dom.js /* global $ $$ $create $root setupLivePrefs waitForSelector */// dom.js
/* global API */// msg.js /* global API */// msg.js
/* global CODEMIRROR_THEMES */ /* global CODEMIRROR_THEMES */
/* global CodeMirror */ /* global CodeMirror */
@ -32,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); $root.classList.toggle('is-new-style', !editor.style.id);
}, },
updateTitle(isDirty = editor.dirty.isDirty()) { updateTitle(isDirty = editor.dirty.isDirty()) {
@ -49,16 +49,14 @@ const editor = {
const baseInit = (() => { const baseInit = (() => {
const domReady = waitForSelector('#sections'); const domReady = waitForSelector('#sections');
const mq = matchMedia('(max-width: 850px)'); const mqCompact = matchMedia('(max-width: 850px)');
(mq.onchange = e => { const toggleCompact = mq => $root.classList.toggle('compact-layout', mq.matches);
document.documentElement.classList.toggle('compact-layout', e.matches); mqCompact.on('change', toggleCompact);
for (const el of $$('details[data-pref]')) { toggleCompact(mqCompact);
el.open = e.matches ? false : prefs.get(el.dataset.pref);
}
})(mq);
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); $root.classList.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);

View File

@ -379,11 +379,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 {
@ -419,10 +419,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;
@ -586,7 +586,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;
} }
@ -948,7 +948,7 @@ 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 */
} }
@ -1020,28 +1020,40 @@ html:not(.usercss) .usercss-only,
/************ reponsive layouts ************/ /************ reponsive layouts ************/
@media(max-width: 850px) { @media(max-width: 850px) {
html:not(.usercss) body { .sectioned body {
height: 100%; height: 100%;
} }
.usercss body { .usercss body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.usercss #header { .usercss #header,
position: inherit; #header:not(.sticky) {
position: static;
} }
#header { #header {
display: block; display: block;
flex: 0 0 auto; flex: 0 0 auto;
height: unset; height: unset;
width: unset; width: 100%;
position: sticky;
background: #fff; background: #fff;
border-right: none; border-right: none;
border-bottom: 1px dashed #AAA; border-bottom: 1px dashed #AAA;
padding: var(--pad05) var(--pad05) 0; padding: var(--pad05) var(--pad05) 0;
} }
#header.sticky {
box-shadow: 0 0 3rem -.75rem black;
}
#header.sticky #basic-info,
#header.sticky #mozilla-format-buttons,
#header.sticky .buttons > button,
#heading, #heading,
#header-sticker {
top: 0;
height: 1px;
position: absolute;
visibility: hidden;
}
h2 { h2 {
display: none; display: none;
} }
@ -1092,7 +1104,6 @@ html:not(.usercss) .usercss-only,
#sections { #sections {
padding-left: 0; padding-left: 0;
} }
/* TODO: switch from .single-editor to the global .usercss */
#sections > :not(.single-editor) { #sections > :not(.single-editor) {
margin: 0 .5rem; margin: 0 .5rem;
padding: .5rem 0; padding: .5rem 0;

View File

@ -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;
@ -74,6 +70,34 @@ 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('#header-sticker');
const scroller = isUsercss ? $('.CodeMirror-scroll') : document.body;
const xo = new IntersectionObserver(onScrolled, {root: isUsercss ? scroller : document});
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([e]) {
const h = $('#header');
const sticky = !e.isIntersecting;
if (!isUsercss) scroller.style.paddingTop = sticky ? h.offsetHeight + 'px' : '';
h.classList.toggle('sticky', sticky);
}
}); });
//#endregion //#endregion

View File

@ -27,6 +27,8 @@ Object.assign(EventTarget.prototype, {
//#region Exports //#region Exports
const $root = document.documentElement;
// 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
@ -474,10 +476,9 @@ const dom = {};
const lazyScripts = [ const lazyScripts = [
'/js/dom-on-load', '/js/dom-on-load',
]; ];
const elHtml = document.documentElement; if (!UA.windows) $root.classList.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 +490,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;
}, },
}); });