feat: add seat firmware workbench and OTA delivery
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
const state = { open: false, returnFocus: null, backgroundInert: [] };
|
||||
|
||||
function setBackgroundInert(inert) {
|
||||
const layer = document.querySelector("#firmwareWorkbenchLayer");
|
||||
if (!layer) return;
|
||||
if (inert) {
|
||||
state.backgroundInert = [...document.body.children]
|
||||
.filter((element) => element !== layer && !["SCRIPT", "STYLE", "LINK"].includes(element.tagName))
|
||||
.map((element) => ({ element, wasInert: element.hasAttribute("inert") }));
|
||||
state.backgroundInert.forEach(({ element }) => element.toggleAttribute("inert", true));
|
||||
return;
|
||||
}
|
||||
state.backgroundInert.forEach(({ element, wasInert }) => element.toggleAttribute("inert", wasInert));
|
||||
state.backgroundInert = [];
|
||||
}
|
||||
|
||||
function setOpen(open) {
|
||||
const next = Boolean(open);
|
||||
const layer = document.querySelector("#firmwareWorkbenchLayer");
|
||||
const trigger = document.querySelector("#firmwareWorkbenchToggle");
|
||||
if (!layer || !trigger || state.open === next) return;
|
||||
state.open = next;
|
||||
trigger.classList.toggle("active", next);
|
||||
trigger.setAttribute("aria-pressed", String(next));
|
||||
trigger.setAttribute("aria-expanded", String(next));
|
||||
trigger.setAttribute("aria-label", next ? "关闭座椅固件开发工作台" : "打开座椅固件开发工作台");
|
||||
trigger.title = next ? "关闭座椅固件开发工作台" : "打开座椅固件开发工作台";
|
||||
if (next) {
|
||||
state.returnFocus = document.activeElement;
|
||||
layer.hidden = false;
|
||||
layer.inert = false;
|
||||
layer.dataset.open = "true";
|
||||
setBackgroundInert(true);
|
||||
requestAnimationFrame(() => document.querySelector("#firmwareWorkbenchClose")?.focus());
|
||||
} else {
|
||||
layer.dataset.open = "false";
|
||||
layer.inert = true;
|
||||
layer.hidden = true;
|
||||
setBackgroundInert(false);
|
||||
const target = state.returnFocus instanceof HTMLElement && state.returnFocus.isConnected
|
||||
? state.returnFocus
|
||||
: trigger;
|
||||
state.returnFocus = null;
|
||||
target.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function mount() {
|
||||
const dock = document.querySelector("#v2PanelControlDock");
|
||||
if (!dock || document.querySelector("#firmwareWorkbenchToggle")) return false;
|
||||
|
||||
const trigger = document.createElement("button");
|
||||
trigger.id = "firmwareWorkbenchToggle";
|
||||
trigger.className = "firmware-workbench-toggle v2-panel-control-button";
|
||||
trigger.type = "button";
|
||||
trigger.setAttribute("aria-controls", "firmwareWorkbenchLayer");
|
||||
trigger.setAttribute("aria-pressed", "false");
|
||||
trigger.setAttribute("aria-expanded", "false");
|
||||
trigger.setAttribute("aria-label", "打开座椅固件开发工作台");
|
||||
trigger.title = "打开座椅固件开发工作台";
|
||||
trigger.innerHTML = '<i data-lucide="blocks"></i><span class="visually-hidden">座椅固件开发工作台</span>';
|
||||
|
||||
const layer = document.createElement("div");
|
||||
layer.id = "firmwareWorkbenchLayer";
|
||||
layer.className = "firmware-workbench-layer";
|
||||
layer.hidden = true;
|
||||
layer.inert = true;
|
||||
layer.setAttribute("role", "dialog");
|
||||
layer.setAttribute("aria-modal", "true");
|
||||
layer.setAttribute("aria-labelledby", "firmwareWorkbenchTitle");
|
||||
layer.innerHTML = `
|
||||
<section class="firmware-workbench-page">
|
||||
<header class="firmware-workbench-header">
|
||||
<div class="firmware-workbench-heading">
|
||||
<span><i data-lucide="blocks"></i></span>
|
||||
<h2 id="firmwareWorkbenchTitle">座椅固件开发工作台</h2>
|
||||
</div>
|
||||
<button id="firmwareWorkbenchClose" type="button" aria-label="关闭座椅固件开发工作台" title="关闭"><i data-lucide="x"></i></button>
|
||||
</header>
|
||||
<iframe class="firmware-workbench-frame" src="/firmware-workbench.html?embedded=1" title="座椅固件开发工作台"></iframe>
|
||||
</section>`;
|
||||
|
||||
dock.append(trigger);
|
||||
document.body.append(layer);
|
||||
trigger.addEventListener("click", () => setOpen(!state.open));
|
||||
document.querySelector("#firmwareWorkbenchClose", layer).addEventListener("click", () => setOpen(false));
|
||||
layer.addEventListener("click", (event) => { if (event.target === layer) setOpen(false); });
|
||||
document.addEventListener("keydown", (event) => {
|
||||
if (state.open && event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
setOpen(false);
|
||||
}
|
||||
});
|
||||
window.lucide?.createIcons({ attrs: { "aria-hidden": "true" } });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mount()) {
|
||||
const observer = new MutationObserver(() => {
|
||||
if (mount()) observer.disconnect();
|
||||
});
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
window.setTimeout(() => observer.disconnect(), 15000);
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user