about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Howell <michael@notriddle.com>2023-09-27 08:59:32 -0700
committerMichael Howell <michael@notriddle.com>2023-10-11 10:26:38 -0700
commit273a302ac8a5fed73bb17988ed571339dc6217ad (patch)
treea5ae54369d04794191387063ca3d228eaf2478c7
parent210c88fc7ae55df147b45b9e75f95874c1a589ef (diff)
downloadrust-273a302ac8a5fed73bb17988ed571339dc6217ad.tar.gz
rust-273a302ac8a5fed73bb17988ed571339dc6217ad.zip
rustdoc: enforce BODY_MIN constraint on sidebar resize
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css19
-rw-r--r--src/librustdoc/html/static/js/main.js113
-rw-r--r--src/librustdoc/html/static/js/storage.js7
-rw-r--r--tests/rustdoc-gui/sidebar-resize-window.goml33
4 files changed, 155 insertions, 17 deletions
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 85a4ff7a621..0bc687da6bf 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -9,6 +9,11 @@
 :root {
 	--nav-sub-mobile-padding: 8px;
 	--search-typename-width: 6.75rem;
+	/* DEFAULT_SIDEBAR_WIDTH
+	   see main.js for information on these values
+	   and on the RUSTDOC_MOBILE_BREAKPOINT */
+	--desktop-sidebar-width: 200px;
+	--src-sidebar-width: 300px;
 }
 
 /* See FiraSans-LICENSE.txt for the Fira Sans license. */
@@ -383,7 +388,7 @@ img {
 
 .sidebar {
 	font-size: 0.875rem;
-	flex: 0 0 var(--desktop-sidebar-width, 200px);
+	flex: 0 0 var(--desktop-sidebar-width);
 	overflow-y: scroll;
 	overscroll-behavior: contain;
 	position: sticky;
@@ -414,7 +419,7 @@ img {
 	position: absolute;
 	height: 100%;
 	/* make sure there's a 1px gap between the scrollbar and resize handle */
-	left: calc(var(--desktop-sidebar-width, 200px) + 1px);
+	left: calc(var(--desktop-sidebar-width) + 1px);
 }
 
 .rustdoc.src .sidebar-resizer {
@@ -426,7 +431,7 @@ img {
 .src-sidebar-expanded .rustdoc.src .sidebar-resizer {
 	/* for src sidebar, gap is already provided by 1px border on sidebar itself, so place resizer
 	   to right of it */
-	left: var(--src-sidebar-width, 300px);
+	left: var(--src-sidebar-width);
 }
 
 .sidebar-resizing {
@@ -448,7 +453,7 @@ img {
 	margin: 0;
 	/* when active or hovered, place resizer glow on top of the sidebar (right next to, or even
 	   on top of, the scrollbar) */
-	left: var(--desktop-sidebar-width, 200px);
+	left: var(--desktop-sidebar-width);
 	border-left: solid 1px var(--sidebar-resizer-hover);
 }
 
@@ -457,7 +462,7 @@ img {
 .src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,
 .src-sidebar-expanded .rustdoc.src .sidebar-resizer.active {
 	/* when active or hovered, place resizer glow on top of the normal src sidebar border */
-	left: calc(var(--src-sidebar-width, 300px) - 1px);
+	left: calc(var(--src-sidebar-width) - 1px);
 }
 
 @media (pointer: coarse) {
@@ -497,7 +502,7 @@ img {
 
 .src-sidebar-expanded .src .sidebar {
 	overflow-y: auto;
-	flex-basis: var(--src-sidebar-width, 300px);
+	flex-basis: var(--src-sidebar-width);
 }
 
 .src-sidebar-expanded .src .sidebar > *:not(#src-sidebar-toggle) {
@@ -1806,7 +1811,7 @@ However, it's not needed with smaller screen width because the doc/code block is
 /*
 WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
 If you update this line, then you also need to update the line with the same warning
-in src-script.js
+in src-script.js and main.js
 */
 @media (max-width: 700px) {
 	/* When linking to an item with an `id` (for instance, by clicking a link in the sidebar,
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 851ee795d4d..e1d674e7d04 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1273,8 +1273,49 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
     searchState.setup();
 }());
 
-// This section handles sidebar resizing
+// Hide, show, and resize the sidebar
+//
+// The body class and CSS variable are initially set up in storage.js,
+// but in this file, we implement:
+//
+//   * the show sidebar button, which appears if the sidebar is hidden
+//     and, by clicking on it, will bring it back
+//   * the sidebar resize handle, which appears only on large viewports
+//     with a [fine precision pointer] to allow the user to change
+//     the size of the sidebar
+//
+// [fine precision pointer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer
 (function() {
+    // 100 is the size of the logo
+    // don't let the sidebar get smaller than that, or it'll get squished
+    const SIDEBAR_MIN = 100;
+    // Don't let the sidebar get bigger than this
+    const SIDEBAR_MAX = 500;
+    // Don't let the body (including the gutter) get smaller than this
+    //
+    // WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
+    // Acceptable values for BODY_MIN are constrained by the mobile breakpoint
+    // (which is the minimum size of the whole page where the sidebar exists)
+    // and the default sidebar width:
+    //
+    //     BODY_MIN <= RUSTDOC_MOBILE_BREAKPOINT - DEFAULT_SIDEBAR_WIDTH
+    //
+    // At the time of this writing, the DEFAULT_SIDEBAR_WIDTH on src pages is
+    // 300px, and the RUSTDOC_MOBILE_BREAKPOINT is 700px, so BODY_MIN must be
+    // at most 400px. Otherwise, it would start out at the default size, then
+    // grabbing the resize handle would suddenly cause it to jank to
+    // its contraint-generated maximum.
+    const BODY_MIN = 400;
+    // At half-way past the minimum size, vanish the sidebar entirely
+    const SIDEBAR_VANISH_THRESHOLD = SIDEBAR_MIN / 2;
+
+    // Toolbar button to show the sidebar.
+    //
+    // On small, "mobile-sized" viewports, it's not persistent and it
+    // can only be activated by going into Settings and hiding the nav bar.
+    // On larger, "desktop-sized" viewports (though that includes many
+    // tablets), it's fixed-position, appears in the left side margin,
+    // and it can be activated by resizing the sidebar into nothing.
     const sidebarButton = document.getElementById("sidebar-button");
     if (sidebarButton) {
         sidebarButton.addEventListener("click", e => {
@@ -1283,13 +1324,38 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
             e.preventDefault();
         });
     }
+
+    // Pointer capture.
+    //
+    // Resizing is a single-pointer gesture. Any secondary pointer is ignored
     let currentPointerId = null;
+
+    // "Desired" sidebar size.
+    //
+    // This is stashed here for window resizing. If the sidebar gets
+    // shrunk to maintain BODY_MIN, and then the user grows the window again,
+    // it gets the sidebar to restore its size.
+    let desiredSidebarSize = null;
+
+    // If this page has no sidebar at all, bail out.
     const resizer = document.querySelector(".sidebar-resizer");
     const sidebar = document.querySelector(".sidebar");
     if (!resizer || !sidebar) {
         return;
     }
+
+    // src page and docs page use different variables, because the contents of
+    // the sidebar are so different that it's reasonable to thing the user
+    // would want them to have different sizes
     const isSrcPage = hasClass(document.body, "src");
+
+    // Call this function to hide the sidebar when using the resize handle
+    //
+    // This function also nulls out the sidebar width CSS variable and setting,
+    // causing it to return to its default. This does not happen if you do it
+    // from settings.js, which uses a separate function. It's done here because
+    // the minimum sidebar size is rather uncomfortable, and it must pass
+    // through that size when using the shrink-to-nothing gesture.
     function hideSidebar() {
         if (isSrcPage) {
             window.rustdocCloseSourceSidebar();
@@ -1302,6 +1368,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
             document.documentElement.style.removeProperty("--desktop-sidebar-width");
         }
     }
+
+    // Call this function to show the sidebar from the resize handle.
+    // On docs pages, this can only happen if the user has grabbed the resize
+    // handle, shrunk the sidebar down to nothing, and then pulls back into
+    // the visible range without releasing it. You can, however, grab the
+    // resize handle on a source page with the sidebar closed, because it
+    // remains visible all the time on there.
     function showSidebar() {
         if (isSrcPage) {
             window.rustdocShowSourceSidebar();
@@ -1310,6 +1383,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
             updateLocalStorage("hide-sidebar", "false");
         }
     }
+
+    // Call this to set the correct CSS variable and setting.
+    // This function doesn't enforce size constraints. Do that before calling it!
     function changeSidebarSize(size) {
         if (isSrcPage) {
             updateLocalStorage("src-sidebar-width", size);
@@ -1319,34 +1395,54 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
             document.documentElement.style.setProperty("--desktop-sidebar-width", size + "px");
         }
     }
+
+    // Check if the sidebar is hidden. Since src pages and doc pages have
+    // different settings, this function has to check that.
     function isSidebarHidden() {
         return isSrcPage ?
             !hasClass(document.documentElement, "src-sidebar-expanded") :
             hasClass(document.documentElement, "hide-sidebar");
     }
+
+    // Respond to the resize handle event.
+    // This function enforces size constraints, and implements the
+    // shrink-to-nothing gesture based on thresholds defined above.
     function resize(e) {
         if (currentPointerId === null || currentPointerId !== e.pointerId) {
             return;
         }
         e.preventDefault();
         const pos = e.clientX - sidebar.offsetLeft - 3;
-        if (pos < 50) {
+        if (pos < SIDEBAR_VANISH_THRESHOLD) {
             hideSidebar();
-        } else if (pos >= 100) {
-            // 100 is the size of the logo
-            // don't let the sidebar get smaller than that, or it'll get squished
+        } else if (pos >= SIDEBAR_MIN) {
             if (isSidebarHidden()) {
                 showSidebar();
             }
-            // don't let the sidebar get wider than 500
-            changeSidebarSize(Math.min(pos, window.innerWidth - 100, 500));
+            // don't let the sidebar get wider than SIDEBAR_MAX, or the body narrower
+            // than BODY_MIN
+            const constrainedPos = Math.min(pos, window.innerWidth - BODY_MIN, SIDEBAR_MAX);
+            changeSidebarSize(constrainedPos);
+            desiredSidebarSize = constrainedPos;
         }
     }
+    // Respond to the window resize event.
+    window.addEventListener("resize", () => {
+        stopResize();
+        if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) {
+            changeSidebarSize(window.innerWidth - BODY_MIN);
+        } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) {
+            changeSidebarSize(desiredSidebarSize);
+        }
+    });
     function stopResize(e) {
         if (currentPointerId === null) {
             return;
         }
-        e.preventDefault();
+        if (e) {
+            e.preventDefault();
+        }
+        desiredSidebarSize = sidebar.getBoundingClientRect().width;
         removeClass(resizer, "active");
         window.removeEventListener("pointermove", resize, false);
         window.removeEventListener("pointerup", stopResize, false);
@@ -1376,6 +1472,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/how-to-read-rustdoc.html\
         window.addEventListener("pointerup", stopResize, false);
         addClass(resizer, "active");
         addClass(document.documentElement, "sidebar-resizing");
+        desiredSidebarSize = null;
     }
     resizer.addEventListener("pointerdown", initResize, false);
 }());
diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js
index 20220f2e69a..18a127fbbd1 100644
--- a/src/librustdoc/html/static/js/storage.js
+++ b/src/librustdoc/html/static/js/storage.js
@@ -196,18 +196,21 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
 
 updateTheme();
 
+// Hide, show, and resize the sidebar at page load time
+//
+// This needs to be done here because this JS is render-blocking,
+// so that the sidebar doesn't "jump" after appearing on screen.
+// The user interaction to change this is set up in main.js.
 if (getSettingValue("source-sidebar-show") === "true") {
     // At this point in page load, `document.body` is not available yet.
     // Set a class on the `<html>` element instead.
     addClass(document.documentElement, "src-sidebar-expanded");
 }
-
 if (getSettingValue("hide-sidebar") === "true") {
     // At this point in page load, `document.body` is not available yet.
     // Set a class on the `<html>` element instead.
     addClass(document.documentElement, "hide-sidebar");
 }
-
 function updateSidebarWidth() {
     const desktopSidebarWidth = getSettingValue("desktop-sidebar-width");
     if (desktopSidebarWidth && desktopSidebarWidth !== "null") {
diff --git a/tests/rustdoc-gui/sidebar-resize-window.goml b/tests/rustdoc-gui/sidebar-resize-window.goml
new file mode 100644
index 00000000000..04321acc105
--- /dev/null
+++ b/tests/rustdoc-gui/sidebar-resize-window.goml
@@ -0,0 +1,33 @@
+// Checks sidebar resizing
+go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 200}, [NEAR])
+
+// resize past maximum (don't grow past 500)
+drag-and-drop: ((205, 100), (600, 100))
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// make the window small enough that the sidebar has to shrink
+set-window-size: (750, 600)
+wait-for-property: (".sidebar", {"clientWidth": 350}, [NEAR])
+
+// grow the window again to make the sidebar bigger
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// make the window small enough that the sidebar has to shrink
+set-window-size: (750, 600)
+wait-for-property: (".sidebar", {"clientWidth": 350}, [NEAR])
+
+// grow the window again to make the sidebar bigger
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
+
+// shrink back down again, then reload the page
+// the "desired size" is a bit of remembered implicit state,
+// and rustdoc tries to minimize things like this
+set-window-size: (800, 600)
+wait-for-property: (".sidebar", {"clientWidth": 400}, [NEAR])
+reload:
+set-window-size: (1280, 600)
+wait-for-property: (".sidebar", {"clientWidth": 400}, [NEAR])