diff --git a/build_files/templates/sidebar/variant-selector.html b/build_files/templates/sidebar/variant-selector.html
index 2b01f1ddc..c27035c89 100644
--- a/build_files/templates/sidebar/variant-selector.html
+++ b/build_files/templates/sidebar/variant-selector.html
@@ -1,32 +1,36 @@
-
+
diff --git a/build_files/theme/css/version_switch.css b/build_files/theme/css/version_switch.css
index be93f946d..12f778575 100644
--- a/build_files/theme/css/version_switch.css
+++ b/build_files/theme/css/version_switch.css
@@ -1,4 +1,4 @@
-#versionwrap {
+#version-menus {
margin: 0;
display: flex;
padding-top: 2px;
@@ -6,18 +6,20 @@
font-size: var(--sidebar-item-font-size);
justify-content: center;
flex-wrap: wrap;
-}
-
-#versionwrap>ul {
list-style: none;
}
-#versionwrap>li {
+#version-menus>li {
display: flex;
width: 50%;
}
-.version-btn {
+version-switch {
+ display: flex;
+ flex-grow: 1;
+}
+
+version-switch button {
display: inline-block;
background-color: var(--color-sidebar-background);
width: 100%;
@@ -32,37 +34,39 @@
z-index: 400;
}
-.version-btn-open::after {
+version-switch.open button::after {
color: gray;
}
-.version-btn:hover,
-.version-btn:focus {
+version-switch button:hover,
+version-switch button:focus {
border-color: #525252;
}
-.version-btn-open {
+version-switch.open button {
color: gray;
border: solid 1px var(--color-sidebar-background-border);
}
-.version-btn.wait {
+version-switch.wait button {
cursor: wait;
}
-.version-btn.disabled {
+version-switch.disabled button {
cursor: not-allowed;
color: dimgray;
}
-.version-dialog {
- display: none;
+version-switch .version-dialog {
+ visibility: hidden;
position: absolute;
bottom: 24px;
width: 50%;
margin: 0 5px;
border-radius: 3px;
box-shadow: 0 0 6px #000C;
+ opacity: 0;
+ transition: visibility 0s linear 300ms, opacity 300ms;
z-index: 999;
max-height: calc(100vh - 30px);
overflow-x: clip;
@@ -70,7 +74,19 @@
cursor: default;
}
-.version-title {
+version-switch.open .version-dialog {
+ visibility: visible;
+ opacity: 1;
+ transition: visibility 0s linear 0s, opacity 200ms;
+}
+
+@media(prefers-reduced-motion: reduce) {
+ version-switch .version-dialog {
+ transition: 0s !important;
+ }
+}
+
+version-switch .version-title {
padding: 5px;
color: var(--color-content-foreground);
text-align: center;
@@ -80,7 +96,7 @@
border-bottom: solid 1.5px var(--color-sidebar-background-border);
}
-.version-list {
+version-switch ul {
padding-left: 0;
margin-top: 0;
margin-bottom: 4px;
@@ -89,9 +105,9 @@
border-radius: 0 0 3px 3px;
}
-.version-list a,
-.version-list span,
-.version-list li {
+version-switch li a,
+version-switch li span,
+version-switch li {
position: relative;
display: block;
font-size: 98%;
@@ -102,18 +118,18 @@
color: var(--color-sidebar-link-text);
}
-.version-list li {
+version-switch li {
background-color: var(--color-sidebar-background);
color: var(--color-sidebar-link-text);
padding: 1px;
}
-.version-list li:hover,
-.version-list li a:focus {
+version-switch li:hover,
+version-switch li a:focus {
background-color: var(--color-background-hover);
}
-.version-list li.selected {
+version-switch li.current {
background: var(--color-sidebar-item-background--current);
font-weight: 700;
-}
\ No newline at end of file
+}
diff --git a/build_files/theme/js/version_switch.js b/build_files/theme/js/version_switch.js
index 5da8949f8..2132004f5 100644
--- a/build_files/theme/js/version_switch.js
+++ b/build_files/theme/js/version_switch.js
@@ -1,4 +1,4 @@
-(function() {//switch: v1.5
+(function() {//switch: v2.0
"use strict";
var versionsFileUrl = "https://docs.blender.org/versions.json"
@@ -28,43 +28,43 @@ var all_langs = {
"zh-hant": "中文(繁體)",
};
-class Popover {
-constructor(id) {
- this.isOpen=false;
- this.type = (id === "version-popover");
- this.btn = document.querySelector('#' + id);
- this.dialog = this.btn.nextElementSibling;
- this.list = this.dialog.querySelector("ul");
- this.sel = null;
+class VersionSwitch extends HTMLElement {
+constructor() {
+ super();
+}
+
+connectedCallback() {
const that = this;
- this.btnClickHandler = function(e) {that.init();e.preventDefault();e.stopPropagation();};
- this.btnKeyHandler = function(e) {if(that.btnKeyFilter(e)){that.init();e.preventDefault();e.stopPropagation();} };
- this.btn.addEventListener("click", this.btnClickHandler);
- this.btn.addEventListener("keydown", this.btnKeyHandler);
+ this.addEventListener("focusout", (e) => {that.focusoutHandler(); e.stopImmediatePropagation();});
+ this.addEventListener("mouseleave", (e) => {that.mouseleaveHandler(e); e.stopImmediatePropagation();});
+ this.firstElementChild.addEventListener("mousedown", (e) => {that.buttonClickHandler(); e.preventDefault()});
+ this.firstElementChild.addEventListener("keydown", (e) => { if(that.buttonKeyFilter(e)){that.buttonClickHandler(); e.preventDefault()}});
+ this.lastElementChild.addEventListener("keydown", (e) => {that.keyHandler(e);});
}
init() {
- this.btn.removeEventListener("click", this.btnClickHandler);
- this.btn.removeEventListener("keydown", this.btnKeyHandler);
-
- new Promise((resolve, reject) => {
+ return new Promise((resolve) => {
if(all_versions === undefined) {
- this.btn.classList.add("wait");
+ this.className = "wait";
fetch(versionsFileUrl)
.then((response) => response.json())
.then((data) => {
all_versions = data;
resolve();
})
- .catch(() => {
- console.error("Version Switch Error: versions.json could not be loaded.");
- this.btn.classList.remove("disabled");
+ .catch((err) => {
+ this.className = "disabled";
+ console.error(err);
});
} else {
resolve();
}
})
.then(() => {
+ if (this.lastElementChild.hasAttribute("data-inited")) {
+ return;
+ }
+ this.lastElementChild.setAttribute("data-inited", true);
let release = DOCUMENTATION_OPTIONS.VERSION;
const m = release.match(/\d\.\d+/g);
if (m) {release = m[0];}
@@ -75,15 +75,7 @@ init() {
const version = this.getNamed(release);
this.buildList(version, lang);
-
- this.list.firstElementChild.remove();
- const that = this;
- this.list.addEventListener("keydown", function(e) {that.keyMove(e);});
-
- this.btn.classList.remove("wait");
- this.btnOpenHandler();
- this.btn.addEventListener("mousedown", function(e){that.btnOpenHandler(); e.preventDefault()});
- this.btn.addEventListener("keydown", function(e){ if(that.btnKeyFilter(e)){that.btnOpenHandler();} });
+ this.className = "closed";
});
}
warnOld(release, all_versions) {
@@ -111,182 +103,188 @@ warnOld(release, all_versions) {
body.prepend(warning);
}
}
-buildList(v, l) {
+buildList(version, lang) {
+ const type = this.getAttribute("type") === "version";
+ const list = this.querySelector("ul");
+ list.firstElementChild.remove();
const url = new URL(window.location.href);
- let pathSplit = ["", "manual", l, v];
+ let pathSplit = ["", "manual", lang, version];
if (url.pathname.startsWith("/manual/")) {
pathSplit.push(url.pathname.split('/').slice(4).join('/'));
} else {
pathSplit.push(url.pathname.substring(1));
}
let dyn, cur;
- if(this.type) {
+ if(type) {
dyn = all_versions;
- cur = v;
+ cur = version;
} else {
dyn = all_langs;
- cur = l;
+ cur = lang;
}
- const that = this;
const template = document.querySelector("template#version-entry").content;
- for (let [ix, title] of Object.entries(dyn)) {
+ for (let [key, value] of Object.entries(dyn)) {
let clone;
- if (ix === cur) {
- clone = template.querySelector("li.selected").cloneNode(true);
- clone.querySelector("span").innerHTML = title;
+ if (key === cur) {
+ clone = template.querySelector("li.current").cloneNode(true);
+ clone.querySelector("span").innerHTML = value;
} else {
- pathSplit[2 + that.type] = ix;
+ pathSplit[2 + type] = key;
let href = new URL(url);
href.pathname = pathSplit.join('/');
clone = template.firstElementChild.cloneNode(true);
const link = clone.querySelector("a");
link.href = href;
- if (that.type) {
- link.innerHTML = title;
+ if (type) {
+ link.innerHTML = value;
} else {
const hint = document.createElement("bdi");
- hint.innerHTML = title;
+ hint.innerHTML = value;
link.appendChild(hint);
}
- link.setAttribute("lang", !that.type ? ix : "en");
+ link.setAttribute("lang", !type ? key : "en");
}
- that.list.append(clone);
+ list.append(clone);
};
- return this.list;
}
-getNamed(v) {
- for (let [ix, title] of Object.entries(all_versions)) {
- if (ix === "dev" || ix === "latest") {
- const m = title.match(/\d\.\d[\w\d\.]*/)[0];
- if (parseFloat(m) == v) {
- v = ix;
- return false;
+getNamed(version) {
+ for (let [key, value] of Object.entries(all_versions)) {
+ if (key === "dev" || key === "latest") {
+ const m = value.match(/\d\.\d[\w\d\.]*/)[0];
+ if (parseFloat(m) == version) {
+ version = key;
+ break;
}
}
- };
- return v;
+ }
+ return version;
}
-dialogToggle(speed) {
- speed = window.matchMedia("(prefers-reduced-motion: reduce)").matches ? 0 : speed;
- const wasClose = !this.isOpen;
- const that = this;
- if(!this.isOpen) {
- this.btn.classList.add("version-btn-open");
- this.btn.setAttribute("aria-pressed", true);
- this.dialog.setAttribute("aria-hidden", false);
- this.dialog.style.display = "block";
- this.dialog.animate({opacity: [0, 1], easing: ['ease-in', 'ease-out']}, speed).finished
- .then(() => {
- this.focusoutHandlerPrime = function(e) {that.focusoutHandler(); e.stopImmediatePropagation();};
- this.mouseoutHandlerPrime = function(e){that.mouseoutHandler(); e.stopImmediatePropagation();};
- this.btn.parentNode.addEventListener("focusout", this.focusoutHandlerPrime);
- this.btn.parentNode.addEventListener("mouseleave", this.mouseoutHandlerPrime);
- });
- this.isOpen = true;
- } else {
- this.btn.classList.remove("version-btn-open");
- this.btn.setAttribute("aria-pressed", false);
- this.dialog.setAttribute("aria-hidden", true);
- this.btn.parentNode.removeEventListener("focusout", this.focusoutHandlerPrime);
- this.btn.parentNode.removeEventListener("mouseleave", this.mouseoutHandlerPrime);
- this.dialog.animate({opacity: [1, 0], easing: ['ease-in', 'ease-out']}, speed).finished
- .then(() => {
- this.dialog.style.display = "none";
- if (this.sel) {this.sel.setAttribute("tabindex", -1);}
- this.btn.setAttribute("tabindex", 0);
+open() {
+ if(this.className === "closed") {
+ this.init()
+ .then(() => {
+ this.className = "open";
+ this.firstElementChild.setAttribute("aria-pressed", true);
+ this.lastElementChild.setAttribute("aria-hidden", false);
+ const s = this.querySelector(".selected");
+ if (s) {
+ s.setAttribute("tabindex", -1);
+ s.classList.remove(".selected");
+ }
if(document.activeElement !== null && document.activeElement !== document && document.activeElement !== document.body) {
- this.btn.focus();
+ const n = this.enter(this.querySelector("ul"));
+ n.classList.add("selected");
+ n.setAttribute("tabindex", 0);
+ n.focus();
}
});
- this.isOpen = false;
}
-
- if(wasClose) {
- if (this.sel) {this.sel.setAttribute("tabindex", -1);}
+}
+close() {
+ if(this.className === "open") {
+ this.className = "closed";
+ const button = this.firstElementChild;
+ button.setAttribute("aria-pressed", false);
+ this.lastElementChild.setAttribute("aria-hidden", true);
+ const s = this.querySelector(".selected");
+ if (s) {
+ s.classList.remove(".selected");
+ s.setAttribute("tabindex", -1);
+ }
+ button.setAttribute("tabindex", 0);
if(document.activeElement !== null && document.activeElement !== document && document.activeElement !== document.body) {
- const nw = this.listEnter();
- nw.setAttribute("tabindex", 0);
- nw.focus();
- this.sel = nw;
+ button.focus();
}
}
}
-btnOpenHandler() {
- this.dialogToggle(300);
+toggle() {
+ if(this.className === "closed") {
+ this.open();
+ } else {
+ this.close();
+ }
+}
+buttonClickHandler() {
+ this.toggle();
}
focusoutHandler() {
- const list = this.list;
const that = this;
- setTimeout(function() {
- if (!list.querySelector(":focus")) {
- that.dialogToggle(200);
+ setTimeout(() => {
+ if (!that.querySelector("ul :focus")) {
+ that.close();
}
}, 200);
}
-mouseoutHandler() {
- this.dialogToggle(200);
+mouseleaveHandler(e) {
+ if (this.contains(e.relatedTarget)) { return; }
+ this.close();
}
-btnKeyFilter(e) {
+buttonKeyFilter(e) {
if (e.ctrlKey || e.shiftKey) {return false;}
if(e.key === " " || e.key === "Enter" || (e.key === "ArrowDown" && e.altKey) || e.key === "ArrowDown" || e.key === "ArrowUp") {
return true;
}
return false;
}
-keyMove(e) {
+keyHandler(e) {
if (e.ctrlKey || e.shiftKey) {return true;}
- let nw = e.target;
+ let n = e.target;
+ const list = this.querySelector("ul");
switch(e.key) {
- case "ArrowUp": nw = this.listPrev(nw); break;
- case "ArrowDown": nw = this.listNext(nw); break;
- case "Home": nw = this.listFirst(); break;
- case "End": nw = this.listLast(); break;
- case "Escape": nw = this.listExit(); break;
- case "ArrowLeft": nw = this.listExit(); break;
- case "ArrowRight": nw = this.listExit(); break;
+ case "ArrowUp": n = this.prev(list, n); break;
+ case "ArrowDown": n = this.next(list, n); break;
+ case "Home": n = this.first(list); break;
+ case "End": n = this.last(list); break;
+ case "Escape": n = this.exit(); break;
+ case "ArrowLeft": n = this.exit(); break;
+ case "ArrowRight": n = this.exit(); break;
default: return false;
}
- nw.setAttribute("tabindex", 0);
- nw.focus();
- if (this.sel) {this.sel.setAttribute("tabindex", -1);}
- this.sel = nw;
+ const s = this.querySelector(".selected");
+ if (s) {
+ s.setAttribute("tabindex", -1);
+ s.classList.remove(".selected");
+ }
+ n.classList.add("selected");
+ n.setAttribute("tabindex", 0);
+ n.focus();
e.preventDefault();
e.stopPropagation();
}
-listPrev(nw) {
- if (nw.parentNode.previousElementSibling.length !== 0) {
- return nw.parentNode.previousElementSibling.firstElementChild;
+prev(list, n) {
+ if (n.parentNode.previousElementSibling) {
+ return n.parentNode.previousElementSibling.firstElementChild;
} else {
- return this.listLast();
+ return this.last(list);
}
}
-listNext(nw) {
- if (nw.parentNode.nextElementSibling.length !== 0) {
- return nw.parentNode.nextElementSibling.firstElementChild;
+next(list, n) {
+ if (n.parentNode.nextElementSibling) {
+ return n.parentNode.nextElementSibling.firstElementChild;
} else {
- return this.listFirst();
+ return this.first(list);
}
}
-listFirst() {
- return this.list.firstElementChild.firstElementChild;
+first(list) {
+ return list.firstElementChild.firstElementChild;
}
-listLast() {
- return this.list.lastElementChild.firstElementChild;
+last(list) {
+ return list.lastElementChild.firstElementChild;
}
-listExit() {
- this.mouseoutHandler();
- return this.btn;
+exit() {
+ this.close();
+ return this.firstElementChild;
}
-listEnter() {
- return this.list.firstElementChild.firstElementChild;
+enter(list) {
+ return list.firstElementChild.firstElementChild;
}
}
+customElements.define("version-switch", VersionSwitch);
+
document.addEventListener('DOMContentLoaded', () => {
let lang = DOCUMENTATION_OPTIONS.LANGUAGE;
if(!lang || lang === "None") {lang = "en";}
- if(all_langs.hasOwnProperty(lang)) {document.querySelector("#lang-popover").innerHTML = all_langs[lang];}
- new Popover("version-popover");
- new Popover("lang-popover");
+ if(all_langs.hasOwnProperty(lang)) {document.querySelector("version-switch[type=lang] button").innerHTML = all_langs[lang];}
});
})();