rustdoc: enforce BODY_MIN constraint on sidebar resize

This commit is contained in:
Michael Howell 2023-09-27 08:59:32 -07:00
parent 210c88fc7a
commit 273a302ac8
4 changed files with 155 additions and 17 deletions

View File

@ -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,

View File

@ -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);
}());

View File

@ -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") {

View File

@ -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])