about summary refs log tree commit diff
path: root/src/librustdoc/html/static/js/main.js
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-09-23 23:49:11 -0400
committerGitHub <noreply@github.com>2024-09-23 23:49:11 -0400
commit0a0ea28f26b37d44a27380713f98b5d9c97f309b (patch)
tree1099fc6e9447d3fa25eca1e853760ea7e47b306a /src/librustdoc/html/static/js/main.js
parent35daf8b8aa95dee4296bad117c4cb7ecaea7bf4f (diff)
parentcc3ffe4c912ff00baeec1f52bb16389136a23a27 (diff)
downloadrust-0a0ea28f26b37d44a27380713f98b5d9c97f309b.tar.gz
rust-0a0ea28f26b37d44a27380713f98b5d9c97f309b.zip
Rollup merge of #129545 - notriddle:notriddle/toolbar-v2, r=GuillaumeGomez
rustdoc: redesign toolbar and disclosure widgets

Fixes #77899
Fixes #90310

## Preview

| before | after
| ------ | -----
| ![image](https://github.com/user-attachments/assets/ebeec185-3a72-481d-921e-a9a885f348d9) | ![image](https://github.com/user-attachments/assets/08735a65-99d1-4523-ab77-ddb164c0a5db)
| ![image](https://github.com/user-attachments/assets/ae8e0f24-49cb-445d-b9bd-cec9c57b94e7) | ![image](https://github.com/user-attachments/assets/ba484f94-b031-41fc-b8a8-6cd81be8fb6b)
| ![image](https://github.com/user-attachments/assets/8c2cc041-a138-4950-a12e-3d529c8a5339) | ![image](https://github.com/user-attachments/assets/e7f010bd-19e2-4711-85bf-3fd00c3e5647)
| ![image](https://github.com/user-attachments/assets/e2b63785-971c-489e-b069-eb85f6a30620) | ![image](https://github.com/user-attachments/assets/b65eea16-d6a3-4aa3-8a27-6ded74009010)
| ![image](https://github.com/user-attachments/assets/1c7b0901-a61a-4325-9d01-9d8b14b476aa) | ![image](https://github.com/user-attachments/assets/d4a485db-d9f1-4a62-94bc-a3d125ea6dc1)
| N/A | ![image](https://github.com/user-attachments/assets/7add0a2a-7fd7-483d-87ee-51ee45a2fe5d)
| ![image](https://github.com/user-attachments/assets/334f50bc-9f8d-42d9-a7df-95058f7cdfd5) | ![image](https://github.com/user-attachments/assets/451fcc22-b034-453c-ae4b-b948fd6bd779)
| ![image](https://github.com/user-attachments/assets/132f720c-802a-466d-bd55-c7a4750acdc3) | ![image](https://github.com/user-attachments/assets/177b7921-06c5-467d-87d3-9cdf88c4e50b)

https://notriddle.com/rustdoc-html-demo-12/toolbar-v2/std/index.html

## Description

This adds labels to the icons and moves them away from the search box.

These changes are made together, because they work together, but are based on several complaints:

* The [+/-] thing are a Reddit-ism. They don't look like buttons, but look like syntax <https://rust-lang.zulipchat.com/#narrow/stream/266220-t-rustdoc/topic/More.20visual.20difference.20for.20the.20.2B.2F-.20.20Icons>, <https://github.com/rust-lang/rust/issues/59851> (some of these are laundry lists with more suggestions, but they all mention [+/-] looking wrong)

* The settings, help, and summary buttons are also too hard to recognize <https://lwn.net/Articles/987070/>, <https://github.com/rust-lang/rust/issues/90310>, <https://github.com/rust-lang/rust/issues/14475#issuecomment-274241997>, <https://internals.rust-lang.org/t/improve-rustdoc-design/12758> ("Not all functionality is self-explanatory, for example the [+] button in the top right corner, the theme picker or the settings button.")

The toggle-all and toggle-individual buttons both need done at once, since we want them to look like they go together. This changes them from both being [+/-] to both being arrows.

CC <https://github.com/rust-lang/rust/pull/113074#issuecomment-1677469680> and ``@jsha`` regarding the use of triangles for disclosure, which is what everyone wanted, but was pending a good toggle-all button. This PR adds a toggle-all button that should work.

Settings and Help are also migrated, so that the whole group can benefit from being described using actual words.

The breadcrumbs also get redesigned, so that they use less space, by shrinking the parent module path parts. This is done at the same time as the toolbar redesign because it's, effectively, moving space from the toolbar to the breadcrumbs.
This is aimed at avoiding any line wrapping at desktop sizes.

## Prior art

This style of toolbar, with explicit labels on the buttons, used to be more popular. It's not very common in web browsers nowadays, and for truly universal icons like :arrow_left: I can understand why, but words are great when icons fail.

![image](https://github.com/user-attachments/assets/9a4a0498-232d-4d60-87b9-f601f4515254)
Diffstat (limited to 'src/librustdoc/html/static/js/main.js')
-rw-r--r--src/librustdoc/html/static/js/main.js141
1 files changed, 77 insertions, 64 deletions
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 0eba80133df..0d556e0a682 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -1,6 +1,6 @@
 // Local js definitions:
 /* global addClass, getSettingValue, hasClass, searchState, updateLocalStorage */
-/* global onEach, onEachLazy, removeClass, getVar */
+/* global onEachLazy, removeClass, getVar */
 
 "use strict";
 
@@ -19,17 +19,25 @@ function resourcePath(basename, extension) {
 
 function hideMain() {
     addClass(document.getElementById(MAIN_ID), "hidden");
+    const toggle = document.getElementById("toggle-all-docs");
+    if (toggle) {
+        toggle.setAttribute("disabled", "disabled");
+    }
 }
 
 function showMain() {
-    removeClass(document.getElementById(MAIN_ID), "hidden");
-}
-
-function blurHandler(event, parentElem, hideCallback) {
-    if (!parentElem.contains(document.activeElement) &&
-        !parentElem.contains(event.relatedTarget)
-    ) {
-        hideCallback();
+    const main = document.getElementById(MAIN_ID);
+    removeClass(main, "hidden");
+    const mainHeading = main.querySelector(".main-heading");
+    if (mainHeading && searchState.rustdocToolbar) {
+        if (searchState.rustdocToolbar.parentElement) {
+            searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar);
+        }
+        mainHeading.appendChild(searchState.rustdocToolbar);
+    }
+    const toggle = document.getElementById("toggle-all-docs");
+    if (toggle) {
+        toggle.removeAttribute("disabled");
     }
 }
 
@@ -167,6 +175,14 @@ function switchDisplayedElement(elemToDisplay) {
     el.appendChild(elemToDisplay);
     hideMain();
     removeClass(el, "hidden");
+
+    const mainHeading = elemToDisplay.querySelector(".main-heading");
+    if (mainHeading && searchState.rustdocToolbar) {
+        if (searchState.rustdocToolbar.parentElement) {
+            searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar);
+        }
+        mainHeading.appendChild(searchState.rustdocToolbar);
+    }
 }
 
 function browserSupportsHistoryApi() {
@@ -194,33 +210,36 @@ function preLoadCss(cssUrl) {
         document.head.append(script);
     }
 
-    getSettingsButton().onclick = event => {
-        if (event.ctrlKey || event.altKey || event.metaKey) {
-            return;
-        }
-        window.hideAllModals(false);
-        addClass(getSettingsButton(), "rotate");
-        event.preventDefault();
-        // Sending request for the CSS and the JS files at the same time so it will
-        // hopefully be loaded when the JS will generate the settings content.
-        loadScript(getVar("static-root-path") + getVar("settings-js"));
-        // Pre-load all theme CSS files, so that switching feels seamless.
-        //
-        // When loading settings.html as a standalone page, the equivalent HTML is
-        // generated in context.rs.
-        setTimeout(() => {
-            const themes = getVar("themes").split(",");
-            for (const theme of themes) {
-                // if there are no themes, do nothing
-                // "".split(",") == [""]
-                if (theme !== "") {
-                    preLoadCss(getVar("root-path") + theme + ".css");
-                }
+    if (getSettingsButton()) {
+        getSettingsButton().onclick = event => {
+            if (event.ctrlKey || event.altKey || event.metaKey) {
+                return;
             }
-        }, 0);
-    };
+            window.hideAllModals(false);
+            addClass(getSettingsButton(), "rotate");
+            event.preventDefault();
+            // Sending request for the CSS and the JS files at the same time so it will
+            // hopefully be loaded when the JS will generate the settings content.
+            loadScript(getVar("static-root-path") + getVar("settings-js"));
+            // Pre-load all theme CSS files, so that switching feels seamless.
+            //
+            // When loading settings.html as a standalone page, the equivalent HTML is
+            // generated in context.rs.
+            setTimeout(() => {
+                const themes = getVar("themes").split(",");
+                for (const theme of themes) {
+                    // if there are no themes, do nothing
+                    // "".split(",") == [""]
+                    if (theme !== "") {
+                        preLoadCss(getVar("root-path") + theme + ".css");
+                    }
+                }
+            }, 0);
+        };
+    }
 
     window.searchState = {
+        rustdocToolbar: document.querySelector("rustdoc-toolbar"),
         loadingText: "Loading search results...",
         input: document.getElementsByClassName("search-input")[0],
         outputElement: () => {
@@ -919,8 +938,7 @@ function preLoadCss(cssUrl) {
                 e.open = true;
             }
         });
-        innerToggle.title = "collapse all docs";
-        innerToggle.children[0].innerText = "\u2212"; // "\u2212" is "−" minus sign
+        innerToggle.children[0].innerText = "Summary";
     }
 
     function collapseAllDocs() {
@@ -934,8 +952,7 @@ function preLoadCss(cssUrl) {
                 e.open = false;
             }
         });
-        innerToggle.title = "expand all docs";
-        innerToggle.children[0].innerText = "+";
+        innerToggle.children[0].innerText = "Show all";
     }
 
     function toggleAllDocs() {
@@ -1330,7 +1347,13 @@ function preLoadCss(cssUrl) {
     }
 
     function helpBlurHandler(event) {
-        blurHandler(event, getHelpButton(), window.hidePopoverMenus);
+        if (!getHelpButton().contains(document.activeElement) &&
+            !getHelpButton().contains(event.relatedTarget) &&
+            !getSettingsButton().contains(document.activeElement) &&
+            !getSettingsButton().contains(event.relatedTarget)
+        ) {
+            window.hidePopoverMenus();
+        }
     }
 
     function buildHelpMenu() {
@@ -1433,9 +1456,13 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
      * Hide all the popover menus.
      */
     window.hidePopoverMenus = () => {
-        onEachLazy(document.querySelectorAll(".search-form .popover"), elem => {
+        onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => {
             elem.style.display = "none";
         });
+        const button = getHelpButton();
+        if (button) {
+            removeClass(button, "help-open");
+        }
     };
 
     /**
@@ -1460,7 +1487,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
     function showHelp() {
         // Prevent `blur` events from being dispatched as a result of closing
         // other modals.
-        getHelpButton().querySelector("a").focus();
+        const button = getHelpButton();
+        addClass(button, "help-open");
+        button.querySelector("a").focus();
         const menu = getHelpMenu(true);
         if (menu.style.display === "none") {
             window.hideAllModals();
@@ -1468,28 +1497,15 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
         }
     }
 
+    const helpLink = document.querySelector(`#${HELP_BUTTON_ID} > a`);
     if (isHelpPage) {
-        showHelp();
-        document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => {
-            // Already on the help page, make help button a no-op.
-            const target = event.target;
-            if (target.tagName !== "A" ||
-                target.parentElement.id !== HELP_BUTTON_ID ||
-                event.ctrlKey ||
-                event.altKey ||
-                event.metaKey) {
-                return;
-            }
-            event.preventDefault();
-        });
-    } else {
-        document.querySelector(`#${HELP_BUTTON_ID} > a`).addEventListener("click", event => {
+        buildHelpMenu();
+    } else if (helpLink) {
+        helpLink.addEventListener("click", event => {
             // By default, have help button open docs in a popover.
             // If user clicks with a moderator, though, use default browser behavior,
             // probably opening in a new window or tab.
-            const target = event.target;
-            if (target.tagName !== "A" ||
-                target.parentElement.id !== HELP_BUTTON_ID ||
+            if (!helpLink.contains(helpLink) ||
                 event.ctrlKey ||
                 event.altKey ||
                 event.metaKey) {
@@ -1812,14 +1828,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
         return;
     }
     but.onclick = () => {
-        const parent = but.parentElement;
         const path = [];
-
-        onEach(parent.childNodes, child => {
-            if (child.tagName === "A") {
-                path.push(child.textContent);
-            }
+        onEachLazy(document.querySelectorAll(".rustdoc-breadcrumbs a"), a => {
+            path.push(a.textContent);
         });
+        path.push(document.querySelector("title").textContent.split(" ")[0]);
 
         copyContentToClipboard(path.join("::"));
         copyButtonAnimation(but);