rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1/* anchor.js — keeps a dropdown's menu on the screen in a browser
2 without CSS anchor positioning. First-party, dependency-free, and
3 app-owned from the moment it is scaffolded.
4
5 A dropdown hugs its summary, and its menu opens toward the leading
6 side. Where the browser has anchor positioning, tokens.css flips the
7 menu when there is no room there, and this file does nothing at all:
8 it tests for the feature first and returns. Where it does not, the
9 menu hangs from the <details>' own box, and nothing in a stylesheet
10 knows where that box is on the screen, so a dropdown at the leading
11 edge would open its menu off it. Here the menu is measured as it
12 opens, and when it would leave the screen and fits better from the
13 summary's other edge, the <details> is marked .is-flip-inline, which
14 tokens.css opens from that edge, and measured again while it is open
15 if the window is resized or turned. Only inline: a menu with no room
16 below still opens below.
17
18 Delete this file and its <script> tag once every browser your app
19 supports has anchor positioning; nothing else changes. Without it, a
20 browser that lacks the feature keeps each dropdown as wide as its
21 container, so the menu opens on the screen but at the container's
22 far edge rather than under its summary. */
23(function () {
24 "use strict";
25
26 if (window.CSS && CSS.supports && CSS.supports("position-area: block-end") && CSS.supports("position-try-fallbacks: flip-block")) return;
27
28 // Told to tokens.css: a dropdown may hug its summary now, because
29 // something here will flip its menu. Without this file it keeps its
30 // container's width, which keeps the menu on the screen unflipped.
31 document.documentElement.setAttribute("data-rst-flip-menus", "");
32
33 // Absolute is the stylesheet's path for a browser without anchor
34 // positioning; a menu nested in another menu is static, and is left
35 // alone. toggle does not bubble; capture still sees it.
36 var DROPDOWN = "[rst-dropdown],.rst-dropdown";
37 var PANEL = ":scope > [rst-dropdown-menu], :scope > .rst-dropdown__menu";
38 var open = [];
39 function overflow(r, w) {
40 return Math.max(0, -r.left) + Math.max(0, r.right - w);
41 }
42 function place(d) {
43 var p = d.querySelector(PANEL), w, before;
44 if (!p || getComputedStyle(p).position !== "absolute") return;
45 d.classList.remove("is-flip-inline");
46 w = document.documentElement.clientWidth;
47 before = overflow(p.getBoundingClientRect(), w);
48 if (!before) return;
49 d.classList.add("is-flip-inline");
50 if (overflow(p.getBoundingClientRect(), w) >= before) d.classList.remove("is-flip-inline");
51 }
52 // A window resized or turned with a menu open can move its summary,
53 // so every open menu is placed again. The listener is there only
54 // while one is open.
55 function replace() {
56 open = open.filter(function (d) { return d.open && d.isConnected; });
57 open.forEach(place);
58 if (!open.length) removeEventListener("resize", replace);
59 }
60 document.addEventListener("toggle", function (e) {
61 var d = e.target;
62 if (!d.matches || !d.matches(DROPDOWN)) return;
63 if (!d.open) return replace();
64 place(d);
65 if (open.indexOf(d) < 0) open.push(d);
66 addEventListener("resize", replace, { passive: true });
67 }, true);
68})();
69