summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Insogna <paolo@cowtech.it>2022-03-22 22:31:56 +0100
committerGitHub <noreply@github.com>2022-03-22 21:31:56 +0000
commit0f79a274621c3fab0d0665fb3715a7b1354896aa (patch)
treeb6571e285ab9dba1ceff3635696bf6c4ec4b3cf3
parent3579f6d0449b3c577627859bf8c5ef700565f970 (diff)
downloadnode-new-0f79a274621c3fab0d0665fb3715a7b1354896aa.tar.gz
doc: make header smaller and dropdown click-driven when JS is on
PR-URL: https://github.com/nodejs/node/pull/42165 Fixes: https://github.com/nodejs/node/issues/42286 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
-rw-r--r--.eslintignore1
-rw-r--r--doc/api_assets/README.md4
-rw-r--r--doc/api_assets/api.js141
-rw-r--r--doc/api_assets/style.css47
-rw-r--r--doc/template.html38
5 files changed, 190 insertions, 41 deletions
diff --git a/.eslintignore b/.eslintignore
index 8ab4750abd..5941496e1a 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -7,4 +7,5 @@ tools/icu
tools/lint-md/lint-md.mjs
benchmark/tmp
doc/**/*.js
+!doc/api_assets/*.js
!.eslintrc.js
diff --git a/doc/api_assets/README.md b/doc/api_assets/README.md
index 07262bba4c..e2c1d90cd0 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 0000000000..4304a25460
--- /dev/null
+++ b/doc/api_assets/api.js
@@ -0,0 +1,141 @@
+'use strict';
+
+{
+ function setupTheme() {
+ 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.documentElement.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.documentElement.classList.add('dark-mode');
+ }
+ } else if (userSettings === 'true') {
+ document.documentElement.classList.add('dark-mode');
+ }
+
+ if (themeToggleButton) {
+ themeToggleButton.hidden = false;
+ themeToggleButton.addEventListener('click', function() {
+ sessionStorage.setItem(
+ kCustomPreference,
+ document.documentElement.classList.toggle('dark-mode')
+ );
+ });
+ }
+ }
+
+ function setupPickers() {
+ function closeAllPickers() {
+ for (const picker of pickers) {
+ picker.parentNode.classList.remove('expanded');
+ }
+
+ window.removeEventListener('click', closeAllPickers);
+ window.removeEventListener('keydown', onKeyDown);
+ }
+
+ function onKeyDown(e) {
+ if (e.key === 'Escape') {
+ closeAllPickers();
+ }
+ }
+
+ const pickers = document.querySelectorAll('.picker-header > a');
+
+ for (const picker of pickers) {
+ const parentNode = picker.parentNode;
+
+ picker.addEventListener('click', (e) => {
+ e.preventDefault();
+
+ /*
+ closeAllPickers as window event trigger already closed all the pickers,
+ if it already closed there is nothing else to do here
+ */
+ if (parentNode.classList.contains('expanded')) {
+ return;
+ }
+
+ /*
+ In the next frame reopen the picker if needed and also setup events
+ to close pickers if needed.
+ */
+
+ requestAnimationFrame(() => {
+ parentNode.classList.add('expanded');
+ window.addEventListener('click', closeAllPickers);
+ window.addEventListener('keydown', onKeyDown);
+ });
+ });
+ }
+ }
+
+ function setupStickyHeaders() {
+ 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 changes 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);
+ }
+
+ function bootstrap() {
+ // Check if we have JavaScript support
+ document.documentElement.classList.add('has-js');
+
+ // Restore user mode preferences
+ setupTheme();
+
+ // Handle pickers with click/taps rather than hovers
+ setupPickers();
+
+ // Track when the header is in sticky position
+ setupStickyHeaders();
+ }
+
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', bootstrap, { once: true });
+ } else {
+ bootstrap();
+ }
+}
diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css
index fa12c02ce7..14302edc06 100644
--- a/doc/api_assets/style.css
+++ b/doc/api_assets/style.css
@@ -189,19 +189,23 @@ li.picker-header .expanded-arrow {
display: none;
}
-li.picker-header:hover .collapsed-arrow {
+li.picker-header.expanded .collapsed-arrow,
+:root:not(.has-js) li.picker-header:hover .collapsed-arrow {
display: none;
}
-li.picker-header:hover .expanded-arrow {
+li.picker-header.expanded .expanded-arrow,
+:root:not(.has-js) li.picker-header:hover .expanded-arrow {
display: inline-block;
}
-li.picker-header:hover > a {
+li.picker-header.expanded > a,
+:root:not(.has-js) li.picker-header:hover > a {
border-radius: 2px 2px 0 0;
}
-li.picker-header:hover > .picker {
+li.picker-header.expanded > .picker,
+:root:not(.has-js) li.picker-header:hover > .picker {
display: block;
z-index: 1;
}
@@ -807,13 +811,38 @@ kbd {
background-color: var(--color-fill-app);
}
-@media not screen, (max-height: 1000px) {
+@media not screen, (max-width: 600px) {
.header {
position: relative;
top: 0;
}
}
+@media not screen, (max-height: 1000px) {
+ :root:not(.has-js) .header {
+ position: relative;
+ top: 0;
+ }
+}
+
+.header .pinned-header {
+ display: none;
+ margin-right: 0.4rem;
+ font-weight: 700;
+}
+
+.header.is-pinned .header-container {
+ display: none;
+}
+
+.header.is-pinned .pinned-header {
+ display: inline;
+}
+
+.header.is-pinned #gtoc {
+ margin: 0;
+}
+
.header-container {
display: flex;
align-items: center;
@@ -845,6 +874,14 @@ kbd {
padding-right: 0;
}
+ .header #gtoc > ul > li.pinned-header {
+ display: none;
+ }
+
+ .header.is-pinned #gtoc > ul > li.pinned-header {
+ display: inline;
+ }
+
#gtoc > ul > li.gtoc-picker-header {
display: none;
}
diff --git a/doc/template.html b/doc/template.html
index 89dd2fbeac..86ba3c9581 100644
--- a/doc/template.html
+++ b/doc/template.html
@@ -9,6 +9,7 @@
<link rel="stylesheet" href="assets/style.css">
<link rel="stylesheet" href="assets/hljs.css">
<link rel="canonical" href="https://nodejs.org/api/__FILENAME__.html">
+ <script async defer src="assets/api.js" type="text/javascript"></script>
</head>
<body class="alt apidoc" id="api-section-__FILENAME__">
<div id="content" class="clearfix">
@@ -39,6 +40,7 @@
</div>
<div id="gtoc">
<ul>
+ <li class="pinned-header">Node.js __VERSION__</li>
__TOC_PICKER__
__GTOC_PICKER__
__ALTDOCS__
@@ -73,41 +75,5 @@
</div>
</div>
</div>
- <script>
- 'use strict';
- {
- 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.documentElement.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.documentElement.classList.add('dark-mode');
- }
- } else if (userSettings === 'true') {
- document.documentElement.classList.add('dark-mode');
- }
- if (themeToggleButton) {
- themeToggleButton.hidden = false;
- themeToggleButton.addEventListener('click', function() {
- sessionStorage.setItem(
- kCustomPreference,
- document.documentElement.classList.toggle('dark-mode')
- );
- });
- }
- }
- </script>
</body>
</html>