From f0ca62093b66f172a8d688f15a60fc933942d8a5 Mon Sep 17 00:00:00 2001 From: Shogun Date: Tue, 1 Feb 2022 10:41:29 +0100 Subject: [PATCH] doc: fix api docs when JS is disabled --- doc/api_assets/README.md | 4 ++ doc/api_assets/api.js | 89 ++++++++++++++++++++++++++++++++++ doc/api_assets/style.css | 30 ++++++------ doc/template.html | 67 +------------------------ test/doctool/test-make-doc.mjs | 3 +- 5 files changed, 112 insertions(+), 81 deletions(-) create mode 100644 doc/api_assets/api.js diff --git a/doc/api_assets/README.md b/doc/api_assets/README.md index 07262bba4ce01a..e2c1d90cd0953f 100644 --- a/doc/api_assets/README.md +++ b/doc/api_assets/README.md @@ -1,5 +1,9 @@ # API Reference Document Assets +## api.js + +The main script for API reference documents. + ## hljs.css The syntax theme for code snippets in API reference documents. diff --git a/doc/api_assets/api.js b/doc/api_assets/api.js new file mode 100644 index 00000000000000..f096d4b9b3edf0 --- /dev/null +++ b/doc/api_assets/api.js @@ -0,0 +1,89 @@ +'use strict'; + +// Check if we have Javascript support +const root = document.querySelector(':root'); +root.classList.remove('no-js'); +root.classList.add('has-js'); + +// Restore user mode preferences +const kCustomPreference = 'customDarkTheme'; +const userSettings = sessionStorage.getItem(kCustomPreference); +const themeToggleButton = document.getElementById('theme-toggle-btn'); +if (userSettings === null && window.matchMedia) { + const mq = window.matchMedia('(prefers-color-scheme: dark)'); + if ('onchange' in mq) { + function mqChangeListener(e) { + document.body.classList.toggle('dark-mode', e.matches); + } + mq.addEventListener('change', mqChangeListener); + if (themeToggleButton) { + themeToggleButton.addEventListener('click', function() { + mq.removeEventListener('change', mqChangeListener); + }, { once: true }); + } + } + if (mq.matches) { + document.body.classList.add('dark-mode'); + } +} else if (userSettings === 'true') { + document.body.classList.add('dark-mode'); +} +if (themeToggleButton) { + themeToggleButton.hidden = false; + themeToggleButton.addEventListener('click', function() { + sessionStorage.setItem( + kCustomPreference, + document.body.classList.toggle('dark-mode') + ); + }); +} + +// Handle pickers with click/taps rather than hovers +const pickers = document.querySelectorAll('.picker-header'); +for(const picker of pickers) { + picker.addEventListener('click', e => { + if (!e.target.closest('.picker')) { + e.preventDefault(); + } + + if (picker.classList.contains('expanded')) { + picker.classList.remove('expanded'); + } else { + for (const other of pickers) { + other.classList.remove('expanded'); + } + + picker.classList.add('expanded'); + } + }); +} + +// Track when the header is in sticky position +const header = document.querySelector(".header"); +let ignoreNextIntersection = false; +new IntersectionObserver( + ([e]) => { + const currentStatus = header.classList.contains('is-pinned'); + const newStatus = e.intersectionRatio < 1; + + // Same status, do nothing + if(currentStatus === newStatus) { + return; + } else if(ignoreNextIntersection) { + ignoreNextIntersection = false; + return; + } + + /* + To avoid flickering, ignore the next change event that is triggered + as the visible elements in the header change once we pin it. + + The timer is reset anyway after few milliseconds + */ + ignoreNextIntersection = true; + setTimeout(() => ignoreNextIntersection = false, 50); + + header.classList.toggle('is-pinned', newStatus); + }, + { threshold: [1] } +).observe(header); \ No newline at end of file diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index 5d3b19a21befa1..1262e2d6b42c59 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -165,10 +165,6 @@ em code { line-height: 1.5rem; } -/* #gtoc li { - white-space: nowrap; -} */ - li.picker-header { position: relative; } @@ -186,19 +182,23 @@ li.picker-header .expanded-arrow { display: none; } -li.picker-header.expanded .collapsed-arrow { +li.picker-header.expanded .collapsed-arrow, +:root.no-js li.picker-header:hover .collapsed-arrow { display: none; } -li.picker-header.expanded .expanded-arrow { +li.picker-header.expanded .expanded-arrow, +:root.no-js li.picker-header:hover .expanded-arrow { display: inline-block; } -li.picker-header.expanded > a { +li.picker-header.expanded > a, +:root.no-js li.picker-header:hover > a { border-radius: 2px 2px 0 0; } -li.picker-header.expanded > .picker { +li.picker-header.expanded > .picker, +:root.no-js li.picker-header:hover > .picker { display: block; z-index: 1; } @@ -797,10 +797,15 @@ kbd { } .header { - position: sticky; - top: -1px; + position: relative; padding-top: 1rem; + background-color: var(--color-fill-app); z-index: 1; + top: -1px; +} + +:root.has-js .header { + position: sticky; } .header .pinner-header { @@ -809,11 +814,6 @@ kbd { font-weight: 700; } -.header.is-pinned { - background-color: var(--color-fill-app); - z-index: 1; -} - .header.is-pinned .header-container { display: none; } diff --git a/doc/template.html b/doc/template.html index 2da95cf1a72347..a21a6cd83cd9ac 100644 --- a/doc/template.html +++ b/doc/template.html @@ -1,5 +1,5 @@ - + @@ -74,69 +74,6 @@

Node.js __VERSION__ documentation

- + diff --git a/test/doctool/test-make-doc.mjs b/test/doctool/test-make-doc.mjs index 06ec6e028bf4e8..54483c7d68932d 100644 --- a/test/doctool/test-make-doc.mjs +++ b/test/doctool/test-make-doc.mjs @@ -40,7 +40,8 @@ const links = toc.match(globalRe); assert.notStrictEqual(links, null); // Filter out duplicate links, leave just filenames, add expected JSON files. -const linkedHtmls = [...new Set(links)].map((link) => link.match(re)[1]); +const linkedHtmls = [...new Set(links)].map((link) => link.match(re)[1]) + .concat(['index.html']); const expectedJsons = linkedHtmls .map((name) => name.replace('.html', '.json')); const expectedDocs = linkedHtmls.concat(expectedJsons);