From e11c4ef755f395a81f0e18240ccd1e4f94126d05 Mon Sep 17 00:00:00 2001 From: tophf Date: Fri, 4 Feb 2022 05:28:05 +0300 Subject: [PATCH] scrollable details + sticky header --- edit.html | 8 +- edit/base.js | 55 ++------- edit/edit.css | 271 +++++++++++++++-------------------------- edit/edit.js | 15 ++- edit/linter-manager.js | 2 +- global.css | 4 +- manifest.json | 2 +- 7 files changed, 125 insertions(+), 232 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..0e9afaf5 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'; /** @@ -56,6 +49,13 @@ const editor = { const baseInit = (() => { 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 { 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 //#region init header diff --git a/edit/edit.css b/edit/edit.css index 2bad83a1..e771619e 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -1,5 +1,6 @@ :root { - --fixed-padding: unset; + --pad: 1rem; + --pad05: calc(0.5 * var(--pad)); } body { @@ -70,7 +71,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 +92,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 +223,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 > * { @@ -426,8 +432,6 @@ input:invalid { } #toc { counter-reset: codelabel; - margin: 0; - padding: .5rem 0; } #toc li { white-space: nowrap; @@ -829,40 +833,13 @@ 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%; } @@ -878,7 +855,7 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high #lint table.empty { display: none; } -#lint caption { +#lint caption:not(:empty) { text-align: left; font-weight: bold; padding-bottom: 6px; @@ -908,6 +885,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; } @@ -970,11 +950,6 @@ html:not(.usercss) .usercss-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,152 +996,100 @@ 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 { + html:not(.usercss) body { + height: 100%; + } + .usercss body { display: flex; flex-direction: column; } + .usercss #header { + position: inherit; + } #header { - flex: 0 1 auto; + display: block; + flex: 0 0 auto; height: unset; width: unset; - position: inherit; + position: sticky; + background: #fff; border-right: none; border-bottom: 1px dashed #AAA; - padding: .5rem 1rem .5rem .5rem; - } - .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; + padding: var(--pad05) var(--pad05) 0; } #heading, h2 { display: none; } #basic-info { - margin-bottom: .5rem; + margin: 0 16px var(--pad05) 0; /* for popup icon in simple window */ 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%; + #details-wrapper details[open] { + max-height: 10vh; } - #sections-list[open] { - max-height: 102px; - } - #sections-list[open] #toc { - max-height: 60px; - overflow-y: auto; - } - #header details:not(#options) { + #details-wrapper[data-open^=".."] details { max-width: 50%; } - .options-column { - flex-grow: 1; - padding-right: .5rem; - box-sizing: border-box; + #details-wrapper[data-open^="..."] { + flex-wrap: wrap; } - .options-column > .usercss-only { - margin-bottom: 0; - } - #options-wrapper .options-column:nth-child(2) { - margin-top: 0; - } - #options:not([open]), + #options[open], + #publish[open], #lint:not([open]) { - overflow: initial; - } - #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; + flex: 0 0 auto; + overflow-x: hidden; } #sections { - height: unset !important; padding-left: 0; - display: flex; - flex-direction: column; - flex: 1; } + /* TODO: switch from .single-editor to the global .usercss */ #sections > :not(.single-editor) { margin: 0 .5rem; padding: .5rem 0; @@ -1175,10 +1098,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..89a945b5 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 */ @@ -43,6 +43,19 @@ baseInit.ready.then(async () => { new MutationObserver(() => elSec.open && editor.updateToc()) .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 => editor.jumpToEditor([...$('#toc').children].indexOf(e.target)); $('#keyMap-help').onclick = () => diff --git a/edit/linter-manager.js b/edit/linter-manager.js index a4c26751..d6b65a71 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; } diff --git a/global.css b/global.css index cbb92c9e..8af35744 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; diff --git a/manifest.json b/manifest.json index a6a4ef9b..c0e75c0c 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "name": "Stylus", "version": "1.5.22", - "minimum_chrome_version": "55", + "minimum_chrome_version": "56", "description": "__MSG_description__", "homepage_url": "https://add0n.com/stylus.html", "manifest_version": 2,