ha-frontend/src/entrypoints/custom-panel.ts

150 lines
4.5 KiB
TypeScript

// Compat needs to be first import
import "../resources/compatibility";
import "../resources/safari-14-attachshadow-patch";
import { CSSResult } from "lit";
import { fireEvent } from "../common/dom/fire_event";
import { isNavigationClick } from "../common/dom/is-navigation-click";
import { loadJS } from "../common/dom/load_resource";
import { webComponentsSupported } from "../common/feature-detect/support-web-components";
import { navigate } from "../common/navigate";
import { CustomPanelInfo } from "../data/panel_custom";
import { baseEntrypointStyles } from "../resources/styles";
import { createCustomPanelElement } from "../util/custom-panel/create-custom-panel-element";
import { loadCustomPanel } from "../util/custom-panel/load-custom-panel";
import { setCustomPanelProperties } from "../util/custom-panel/set-custom-panel-properties";
import("@polymer/polymer/lib/utils/settings").then(
({ setCancelSyntheticClickEvents }) => setCancelSyntheticClickEvents(false)
);
declare global {
interface Window {
loadES5Adapter: () => Promise<unknown>;
}
}
let es5Loaded: Promise<unknown> | undefined;
window.loadES5Adapter = () => {
if (!es5Loaded) {
es5Loaded = loadJS(
`${__STATIC_PATH__}polyfills/custom-elements-es5-adapter.js`
).catch(); // Swallow errors as it raises errors on old browsers.
}
return es5Loaded;
};
let panelEl: HTMLElement | undefined;
function setProperties(properties) {
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;
let start: Promise<unknown> = Promise.resolve();
if (!webComponentsSupported) {
start = start.then(() =>
loadJS(`${__STATIC_PATH__}polyfills/webcomponents-bundle.js`)
);
}
if (__BUILD__ === "es5") {
start = start.then(() => window.loadES5Adapter());
}
start
.then(() => loadCustomPanel(config))
// If our element is using es5, let it finish loading that and define element
// This avoids elements getting upgraded after being added to the DOM
.then(() => es5Loaded || Promise.resolve())
.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);
}
});
}
document.addEventListener(
"DOMContentLoaded",
() => window.parent.customPanel!.registerIframe(initialize, setProperties),
{ once: true }
);
window.addEventListener("unload", () => {
// allow disconnected callback to fire
while (document.body.lastChild) {
document.body.removeChild(document.body.lastChild);
}
});