Files
frontend/src/entrypoints/custom-panel.ts
T
Maarten Lakerveld 46de23727f Remove ES5 specific code (#53899)
* Remove web components polyfills and ES5 custom elements adapter

All supported browsers (legacy floor: Chrome 59, Safari/iOS 12, Firefox 94)
have native shadow DOM and custom elements, so the webcomponents bundle,
ShadyCSS branch, and lit polyfill-support are unreachable. The legacy build
now emits ES2017 classes, so custom-elements-es5-adapter and the
window.loadES5Adapter hook (no known third-party consumers) are removed.
Also stops shipping the never-loaded dialog-polyfill css and drops both
now-unused dependencies.

* Remove keyed-es5 Terser workaround

The custom keyed directive existed because Terser with ecma: 5 miscompiled
the destructured update() parameters (#28732). The legacy build now minifies
with ecma: 2017, so the stock lit-html keyed directive works in both builds.

* Remove old-browser JS shims and stale ES5 build references

Drops the IE-only navigator.msMaxTouchPoints check, replaces the
toggleAttribute helper with the native method (polyfilled automatically for
Chrome < 69 in the legacy build), and removes babel excludes for the
uninstalled proxy-polyfill and unfetch packages. Updates comments that
still described the legacy build as ES5.

* Remove vendor prefixes for no-longer-supported browsers

Deletes -ms- prefixes (IE/EdgeHTML only) and -webkit-/-moz- prefixed
declarations that every supported browser understands unprefixed, or that
Lightning CSS re-adds automatically from the unprefixed property in
production builds. Blocks that only had prefixed user-select now use the
standard property (previously Firefox got no user-select there at all).
Converts the four -webkit-linear-gradient() declarations - the sole
gradient syntax on those sliders - to standard linear-gradient().

* Remove dead html_url custom panel support

html_url pointed to an HTML Import, a Polymer-era feature removed from
Chrome in 2019 and never shipped elsewhere. The loader has not handled the
html type for years (it fell through to a rejection), and core's
panel_custom integration no longer accepts the option, so the branch was
unreachable. Also drops the ha-panel-${name} legacy tag naming that was
keyed on html_url.

* Repair list-plugins-and-polyfills script for Babel 8

The audit script died at startup since the Babel 8 update: preset-env no
longer exposes lib/debug.js (logPlugin is now inlined locally, built on the
public getInclusionReasons helper) and babel-plugin-polyfill-corejs3 v1 no
longer ships lib/shipped-proposals.js (list inlined).

Instead of invoking the preset with a hand-mocked plugin API - the part
that kept drifting - the plugin listing now runs a real transform of an
empty file with preset-env in debug mode, declaring the same caller
capabilities as babel-loader. The polyfill listing now passes the
configured core-js version to core-js-compat, mirroring the provider's own
filtering so the report cannot list modules the installed core-js lacks.
Output is byte-identical to the direct-invocation approach. Also documents
the script in the build-scripts README.

* Correct macOS floor for Safari 26 in companion app UA regex

Safari 26 ships for macOS 14 Sonoma and 15 Sequoia, not only macOS 26, so
the SAFARI_TO_MACOS entry breaking the minimum-supported-macOS pattern
would have sent updated macOS 14/15 companion apps to the legacy build
once the modern floor reaches Safari 26.

* Remove orphaned values left behind by vendor prefix removal

* Require macOS 14.6 for Safari 26 in companion app UA regex
2026-09-02 08:15:36 +03:00

129 lines
4.0 KiB
TypeScript

import type { Connection } from "home-assistant-js-websocket";
import type { CSSResult } from "lit";
import { fireEvent } from "../common/dom/fire_event";
import { isNavigationClick } from "../common/dom/is-navigation-click";
import { navigate } from "../common/navigate";
import type { CustomPanelInfo } from "../data/panel_custom";
import { baseEntrypointStyles } from "../resources/styles";
import { createCustomPanelElement } from "../util/custom-panel/create-custom-panel-element";
import { dropRealmCollections } from "../util/custom-panel/drop-realm-collections";
import { loadCustomPanel } from "../util/custom-panel/load-custom-panel";
import { setCustomPanelProperties } from "../util/custom-panel/set-custom-panel-properties";
let panelEl: HTMLElement | undefined;
let initialized = false;
// Kept so we can clean up after ourselves on pagehide without depending on
// `window.parent.customPanel`, which `_cleanupPanel()` may already have deleted.
let connection: Connection | undefined;
function setProperties(properties) {
if (properties.hass?.connection) {
connection = properties.hass.connection;
}
if (!panelEl) {
return;
}
setCustomPanelProperties(panelEl, properties);
}
function initialize(
panel: CustomPanelInfo,
properties: Record<string, unknown>
) {
const style = document.createElement("style");
style.innerHTML = `
body {
margin:0;
background-color: var(--primary-background-color, #fafafa);
color: var(--primary-text-color, #212121);
}
@media (prefers-color-scheme: dark) {
body {
background-color: var(--primary-background-color, #111111);
color: var(--primary-text-color, #e1e1e1);
}
}`;
document.head.appendChild(style);
const config = panel.config._panel_custom;
loadCustomPanel(config).then(
() => {
panelEl = createCustomPanelElement(config);
const forwardEvent = (ev) => {
if (window.parent.customPanel) {
fireEvent(window.parent.customPanel, ev.type, ev.detail);
}
};
panelEl!.addEventListener("hass-toggle-menu", forwardEvent);
window.addEventListener("location-changed", (ev: any) => {
if (window.parent.customPanel) {
window.parent.customPanel.navigate(
window.location.pathname,
ev.detail
);
}
});
setProperties({ panel, ...properties });
document.body.appendChild(panelEl!);
},
(err) => {
// eslint-disable-next-line
console.error(err, panel);
let errorScreen;
if (panel.url_path === "hassio") {
import("../layouts/supervisor-error-screen");
errorScreen = document.createElement("supervisor-error-screen") as any;
} else {
import("../layouts/hass-error-screen");
errorScreen = document.createElement("hass-error-screen") as any;
errorScreen.error = `Unable to load the panel source: ${err}.`;
}
const errorStyle = document.createElement("style");
errorStyle.innerHTML = (baseEntrypointStyles as CSSResult).cssText;
document.body.appendChild(errorStyle);
errorScreen.hass = properties.hass;
document.body.appendChild(errorScreen);
}
);
document.body.addEventListener("click", (ev) => {
const href = isNavigationClick(ev);
if (href) {
navigate(href);
}
});
}
function handleReady() {
if (initialized) return;
initialized = true;
window.parent.customPanel!.registerIframe(initialize, setProperties);
}
// Initial load
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", handleReady, { once: true });
} else {
handleReady();
}
window.addEventListener("pageshow", handleReady);
window.addEventListener("pagehide", () => {
initialized = false;
// allow disconnected callback to fire
while (document.body.lastChild) {
document.body.removeChild(document.body.lastChild);
}
// The connection is owned by the main window and outlives this realm, so any
// collection we created on it has to go with us.
if (connection) {
dropRealmCollections(connection);
}
});