diff --git a/static/warnings.js b/static/warnings.js new file mode 100644 index 000000000..799f55cad --- /dev/null +++ b/static/warnings.js @@ -0,0 +1,125 @@ +(function() { + function load(key, def = null) { + const value = window.localStorage.getItem(key); + if (value) { + try { + return JSON.parse(value); + } catch (ex) { + console.error(`Failed loading ${key} from local storage`, ex); + return def; + } + } else { + return def; + } + } + + function store(key, value) { + window.localStorage.setItem(key, JSON.stringify(value)); + } + + function create(tagName, attrs = {}, children = [], listeners = {}) { + const el = document.createElement(tagName); + for (const key of Object.keys(attrs)) { + if (typeof attrs[key] === "object") { + for (const subkey of Object.keys(attrs[key])) { + el[key].setProperty(subkey, attrs[key][subkey]); + } + } else { + el.setAttribute(key, attrs[key]); + } + } + el.append(...children); + for (const key of Object.keys(listeners)) { + el.addEventListener(key, listeners[key]); + } + return el; + } + + + if (!load("docs-rs-warnings-enabled", false)) { + return; + } + + const parentEl = document.getElementById("warnings-menu-parent"); + parentEl.removeAttribute("hidden"); + + const current = JSON.parse(document.getElementById("crate-metadata")?.innerText || null); + + const menuEl = document.getElementById("warnings-menu"); + + const followed = load("docs-rs-warnings-followed", []); + + function update() { + const children = []; + + if (followed.length > 0) { + children.push( + create("div", { class: "pure-g" }, [ + create("div", { class: "pure-u-1" }, [ + create("ul", { class: "pure-menu-list", style: { width: "100%" } }, [ + create("li", { class: "pure-menu-heading" }, [ + create("b", {}, ["Followed crates"]), + ]), + ...followed.map(name => ( + create("li", { class: "pure-menu-item followed" }, [ + create("a", { class: "pure-menu-link", href: `/${name}` }, [ + name, + ]), + create( + "a", + { class: "pure-menu-link remove", href: "#" }, + ["🗙"], + { + click: _ => { + const index = followed.indexOf(name); + followed.splice(index, 1); + store("docs-rs-warnings-followed", followed); + update(); + }, + }, + ), + ]) + )), + ]), + ]), + ]), + ); + } + + if (current && !followed.includes(current.name)) { + children.push( + create("div", { class: "pure-g" }, [ + create("div", { class: "pure-u-1" }, [ + create("ul", { class: "pure-menu-list", style: { width: "100%" } }, [ + create("li", { class: "pure-menu-item" }, [ + create("a", { class: "pure-menu-link", href: "#" }, [ + "Follow ", + create("b", {}, [current.name]), + ], { + click: () => { + const i = followed.findIndex(name => name > current.name); + if (i >= 0) { + followed.splice(i, 0, current.name); + } else { + followed.push(current.name); + } + store("docs-rs-warnings-followed", followed); + update(); + }, + }), + ]), + ]), + ]), + ]), + ); + } + + for (const child of children.slice(0, -1)) { + child.classList.add("menu-item-divided"); + } + + menuEl.replaceChildren(...children); + } + + update(); +})(); diff --git a/templates/base.html b/templates/base.html index 8085c8b39..b2ebad5ed 100644 --- a/templates/base.html +++ b/templates/base.html @@ -24,6 +24,7 @@ <script defer type="text/javascript" nonce="{{ csp_nonce }}" src="/-/static/menu.js?{{ build_slug }}"></script> <script defer type="text/javascript" nonce="{{ csp_nonce }}" src="/-/static/index.js?{{ build_slug }}"></script> + <script defer type="text/javascript" nonce="{{ csp_nonce }}" src="/-/static/warnings.js?{{ build_slug }}"></script> </head> <body class="{% block body_classes %}{% endblock body_classes %}"> diff --git a/templates/header/topbar_end.html b/templates/header/topbar_end.html index 92b7cfa4e..5723f5303 100644 --- a/templates/header/topbar_end.html +++ b/templates/header/topbar_end.html @@ -4,6 +4,8 @@ {# The global alert, if there is one #} {% include "header/global_alert.html" -%} + {% include "header/warnings.html" -%} + <ul class="pure-menu-list"> {# The Rust dropdown menu diff --git a/templates/header/warnings.html b/templates/header/warnings.html new file mode 100644 index 000000000..4c9d173a8 --- /dev/null +++ b/templates/header/warnings.html @@ -0,0 +1,7 @@ +<ul id="warnings-menu-parent" class="pure-menu-list" hidden> + <li class="pure-menu-item pure-menu-has-children"> + <a href="#" class="pure-menu-link" aria-label="Warnings">Warnings <span id="warnings-count"></span></a> + <div id="warnings-menu" class="pure-menu-children"> + </div> + </li> +</ul> diff --git a/templates/rustdoc/body.html b/templates/rustdoc/body.html index 365be5086..f4382bfb7 100644 --- a/templates/rustdoc/body.html +++ b/templates/rustdoc/body.html @@ -1,5 +1,6 @@ {%- set build_slug = slug::slugify(crate::BUILD_VERSION) -%} <script async src="/-/static/menu.js?{{ build_slug }}"></script> <script async src="/-/static/index.js?{{ build_slug }}"></script> +<script async src="/-/static/warnings.js?{{ build_slug }}"></script> {# see comment in ../storage-change-detection.html for details #} <iframe src="/-/storage-change-detection.html" width="0" height="0" style="display: none"></iframe> diff --git a/templates/style/_navbar.scss b/templates/style/_navbar.scss index bd173f8ea..9a85e0170 100644 --- a/templates/style/_navbar.scss +++ b/templates/style/_navbar.scss @@ -362,6 +362,22 @@ div.nav-container { } } } + + #warnings-menu { + .followed { + display: flex; + align-items: center; + :first-child { + flex: auto; + } + } + .remove { + padding: 0 .5em; + &:hover { + color: var(--color-error); + } + } + } } #nav-search, #nav-sort {