scrollable details + sticky header

This commit is contained in:
tophf 2022-02-04 05:28:05 +03:00
parent 2e021f6ac9
commit e11c4ef755
7 changed files with 125 additions and 232 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';
/** /**
@ -56,6 +49,13 @@ const editor = {
const baseInit = (() => { const baseInit = (() => {
const domReady = waitForSelector('#sections'); const domReady = waitForSelector('#sections');
const mq = matchMedia('(max-width: 850px)');
(mq.onchange = e => {
document.documentElement.classList.toggle('compact-layout', e.matches);
for (const el of $$('details[data-pref]')) {
el.open = e.matches ? false : prefs.get(el.dataset.pref);
}
})(mq);
return { return {
domReady, domReady,
@ -120,45 +120,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,6 @@
:root { :root {
--fixed-padding: unset; --pad: 1rem;
--pad05: calc(0.5 * var(--pad));
} }
body { body {
@ -70,7 +71,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 +92,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 +223,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 > * {
@ -426,8 +432,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;
@ -829,40 +833,13 @@ 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%;
} }
@ -878,7 +855,7 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
#lint table.empty { #lint table.empty {
display: none; display: none;
} }
#lint caption { #lint caption:not(:empty) {
text-align: left; text-align: left;
font-weight: bold; font-weight: bold;
padding-bottom: 6px; padding-bottom: 6px;
@ -908,6 +885,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;
} }
@ -970,11 +950,6 @@ html:not(.usercss) .usercss-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,152 +996,100 @@ 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 { html:not(.usercss) body {
height: 100%;
}
.usercss body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.usercss #header {
position: inherit;
}
#header { #header {
flex: 0 1 auto; display: block;
flex: 0 0 auto;
height: unset; height: unset;
width: unset; width: unset;
position: inherit; position: sticky;
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 {
--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;
} }
#heading, #heading,
h2 { h2 {
display: none; display: none;
} }
#basic-info { #basic-info {
margin-bottom: .5rem; margin: 0 16px var(--pad05) 0; /* for popup icon in simple window */
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%; max-height: 10vh;
} }
#sections-list[open] { #details-wrapper[data-open^=".."] details {
max-height: 102px;
}
#sections-list[open] #toc {
max-height: 60px;
overflow-y: auto;
}
#header details:not(#options) {
max-width: 50%; max-width: 50%;
} }
.options-column { #details-wrapper[data-open^="..."] {
flex-grow: 1; flex-wrap: wrap;
padding-right: .5rem;
box-sizing: border-box;
} }
.options-column > .usercss-only { #options[open],
margin-bottom: 0; #publish[open],
}
#options-wrapper .options-column:nth-child(2) {
margin-top: 0;
}
#options:not([open]),
#lint:not([open]) { #lint:not([open]) {
overflow: initial; flex: 0 0 auto;
} overflow-x: hidden;
#options:not([open]) + #lint:not([open]) {
margin-top: 0;
}
#lint summary {
position: static;
margin-bottom: 0;
}
#header summary {
margin-left: 0;
padding-left: 4px;
}
#header summary h2 {
margin: 0;
padding: 0;
}
.option label {
margin: 0;
}
#options [type="number"] {
text-align: left; /* workaround the column flow bug in webkit */
padding-left: 0.2rem;
}
#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;
} }
#sections { #sections {
height: unset !important;
padding-left: 0; padding-left: 0;
display: flex;
flex-direction: column;
flex: 1;
} }
/* 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;
@ -1175,10 +1098,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 */
@ -43,6 +43,19 @@ baseInit.ready.then(async () => {
new MutationObserver(() => elSec.open && editor.updateToc()) new MutationObserver(() => elSec.open && editor.updateToc())
.observe(elSec, {attributes: true, attributeFilter: ['open']}); .observe(elSec, {attributes: true, attributeFilter: ['open']});
// Auto-update `data-open` attribute to the number of open details
{
const wrapper = $('#details-wrapper');
const details = $$('details', wrapper);
const ds = wrapper.dataset;
const update = () => {
ds.open = '.'.repeat(details.reduce((res, el) => res + (el.open ? 1 : 0), 0));
};
for (const el of details) {
new MutationObserver(update).observe(el, {attributes: true, attributeFilter: ['open']});
}
}
$('#toc').onclick = e => $('#toc').onclick = e =>
editor.jumpToEditor([...$('#toc').children].indexOf(e.target)); editor.jumpToEditor([...$('#toc').children].indexOf(e.target));
$('#keyMap-help').onclick = () => $('#keyMap-help').onclick = () =>

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;
} }

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;

View File

@ -1,7 +1,7 @@
{ {
"name": "Stylus", "name": "Stylus",
"version": "1.5.22", "version": "1.5.22",
"minimum_chrome_version": "55", "minimum_chrome_version": "56",
"description": "__MSG_description__", "description": "__MSG_description__",
"homepage_url": "https://add0n.com/stylus.html", "homepage_url": "https://add0n.com/stylus.html",
"manifest_version": 2, "manifest_version": 2,