410 lines
12 KiB
TypeScript
410 lines
12 KiB
TypeScript
import { LitElement, html, css } from "lit";
|
|
import { property, query } from "lit/decorators.js";
|
|
import { unsafeHTML } from "lit/directives/unsafe-html.js";
|
|
import { provideHass, loadLoadCardHelpers, hass_base_el } from "../helpers";
|
|
import { loadHaForm } from "../helpers";
|
|
|
|
class BrowserModPopup extends LitElement {
|
|
@property() open;
|
|
@property() content;
|
|
@property() title;
|
|
@property({ reflect: true }) actions;
|
|
@property({ reflect: true }) card;
|
|
@property() right_button;
|
|
@property() left_button;
|
|
@property() dismissable;
|
|
@property({ reflect: true }) wide;
|
|
@property({ reflect: true }) fullscreen;
|
|
@property() _style;
|
|
@query("ha-dialog") dialog: any;
|
|
_autoclose;
|
|
_autocloseListener;
|
|
_actions;
|
|
timeout;
|
|
_timeoutStart;
|
|
_timeoutTimer;
|
|
_resolveClosed;
|
|
_formdata;
|
|
|
|
async closeDialog() {
|
|
this.open = false;
|
|
clearInterval(this._timeoutTimer);
|
|
if (this._autocloseListener) {
|
|
window.browser_mod.removeEventListener(
|
|
"browser-mod-activity",
|
|
this._autocloseListener
|
|
);
|
|
this._autocloseListener = undefined;
|
|
}
|
|
}
|
|
|
|
openDialog() {
|
|
this.open = true;
|
|
this.dialog?.show();
|
|
if (this.timeout) {
|
|
this._timeoutStart = new Date().getTime();
|
|
this._timeoutTimer = setInterval(() => {
|
|
const ellapsed = new Date().getTime() - this._timeoutStart;
|
|
const progress = (ellapsed / this.timeout) * 100;
|
|
this.style.setProperty("--progress", `${progress}%`);
|
|
if (ellapsed >= this.timeout) this._timeout();
|
|
}, 10);
|
|
}
|
|
this._autocloseListener = undefined;
|
|
if (this._autoclose) {
|
|
this._autocloseListener = () => this.dialog.close();
|
|
window.browser_mod.addEventListener(
|
|
"browser-mod-activity",
|
|
this._autocloseListener,
|
|
{ once: true }
|
|
);
|
|
}
|
|
}
|
|
|
|
async setupDialog(
|
|
title,
|
|
content,
|
|
{
|
|
right_button = undefined,
|
|
right_button_action = undefined,
|
|
left_button = undefined,
|
|
left_button_action = undefined,
|
|
dismissable = true,
|
|
dismiss_action = undefined,
|
|
timeout = undefined,
|
|
timeout_action = undefined,
|
|
size = undefined,
|
|
style = undefined,
|
|
autoclose = false,
|
|
} = {}
|
|
) {
|
|
this._formdata = undefined;
|
|
this.title = title;
|
|
this.card = undefined;
|
|
if (content && content instanceof HTMLElement) {
|
|
this.content = content;
|
|
} else if (content && Array.isArray(content)) {
|
|
loadHaForm();
|
|
const form: any = document.createElement("ha-form");
|
|
form.schema = content;
|
|
form.computeLabel = (s) => s.label ?? s.name;
|
|
form.hass = window.browser_mod.hass;
|
|
this._formdata = {};
|
|
for (const i of content) {
|
|
if (i.name && i.default !== undefined) {
|
|
this._formdata[i.name] = i.default;
|
|
}
|
|
}
|
|
form.data = this._formdata;
|
|
provideHass(form);
|
|
form.addEventListener("value-changed", (ev) => {
|
|
this._formdata = { ...ev.detail.value };
|
|
form.data = this._formdata;
|
|
});
|
|
this.content = form;
|
|
} else if (content && typeof content === "object") {
|
|
// Create a card from config in content
|
|
this.card = true;
|
|
const helpers = await window.loadCardHelpers();
|
|
const card = await helpers.createCardElement(content);
|
|
card.hass = window.browser_mod.hass;
|
|
provideHass(card);
|
|
this.content = card;
|
|
} else {
|
|
// Basic HTML content
|
|
this.content = unsafeHTML(content);
|
|
}
|
|
|
|
this.right_button = right_button;
|
|
this.left_button = left_button;
|
|
this.actions = right_button === undefined ? undefined : "";
|
|
|
|
this.dismissable = dismissable;
|
|
this.timeout = timeout;
|
|
this._actions = {
|
|
right_button_action,
|
|
left_button_action,
|
|
dismiss_action,
|
|
timeout_action,
|
|
};
|
|
this.wide = size === "wide" ? "" : undefined;
|
|
this.fullscreen = size === "fullscreen" ? "" : undefined;
|
|
this._style = style;
|
|
this._autoclose = autoclose;
|
|
}
|
|
|
|
async _primary() {
|
|
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
|
|
this.dialog?.close();
|
|
this._actions?.right_button_action?.(this._formdata);
|
|
}
|
|
async _secondary() {
|
|
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
|
|
this.dialog?.close();
|
|
this._actions?.left_button_action?.(this._formdata);
|
|
}
|
|
async _dismiss(ev?) {
|
|
this.dialog?.close();
|
|
this._actions?.dismiss_action?.();
|
|
}
|
|
async _timeout() {
|
|
if (this._actions?.dismiss_action) this._actions.dismiss_action = undefined;
|
|
this.dialog?.close();
|
|
this._actions?.timeout_action?.();
|
|
}
|
|
|
|
render() {
|
|
if (!this.open) return html``;
|
|
|
|
return html`
|
|
<ha-dialog
|
|
open
|
|
@closed=${this.closeDialog}
|
|
@closing=${this._dismiss}
|
|
.heading=${this.title !== undefined}
|
|
?hideActions=${this.actions === undefined}
|
|
.scrimClickAction=${this.dismissable ? "close" : ""}
|
|
.escapeKeyAction=${this.dismissable ? "close" : ""}
|
|
>
|
|
${this.timeout
|
|
? html` <div slot="heading" class="progress"></div> `
|
|
: ""}
|
|
${this.title
|
|
? html`
|
|
<div slot="heading">
|
|
<ha-header-bar>
|
|
${this.dismissable
|
|
? html`
|
|
<ha-icon-button
|
|
dialogAction="cancel"
|
|
slot="navigationIcon"
|
|
>
|
|
<ha-icon .icon=${"mdi:close"}></ha-icon>
|
|
</ha-icon-button>
|
|
`
|
|
: ""}
|
|
<div slot="title" class="main-title">${this.title}</div>
|
|
</ha-header-bar>
|
|
</div>
|
|
`
|
|
: html``}
|
|
|
|
<div class="content">${this.content}</div>
|
|
|
|
${this.right_button !== undefined
|
|
? html`
|
|
<mwc-button
|
|
slot="primaryAction"
|
|
.label=${this.right_button}
|
|
@click=${this._primary}
|
|
class="action-button"
|
|
></mwc-button>
|
|
`
|
|
: ""}
|
|
${this.left_button !== undefined
|
|
? html`
|
|
<mwc-button
|
|
slot="secondaryAction"
|
|
.label=${this.left_button}
|
|
@click=${this._secondary}
|
|
class="action-button"
|
|
></mwc-button>
|
|
`
|
|
: ""}
|
|
<style>
|
|
:host {
|
|
${this._style}
|
|
}
|
|
</style>
|
|
</ha-dialog>
|
|
`;
|
|
}
|
|
|
|
static get styles() {
|
|
return css`
|
|
ha-dialog {
|
|
--dialog-backdrop-filter: blur(5px);
|
|
z-index: 10;
|
|
--mdc-dialog-min-width: var(--popup-min-width, 400px);
|
|
--mdc-dialog-max-width: var(--popup-max-width, 600px);
|
|
--mdc-dialog-heading-ink-color: var(--primary-text-color);
|
|
--mdc-dialog-content-ink-color: var(--primary-text-color);
|
|
--justify-action-buttons: space-between;
|
|
|
|
--dialog-box-shadow: 0px 0px 0px
|
|
var(--popup-border-width, var(--ha-card-border-width, 2px))
|
|
var(
|
|
--popup-border-color,
|
|
var(--ha-card-border-color, var(--divider-color, #e0e0e0))
|
|
);
|
|
--mdc-theme-surface: var(
|
|
--popup-background-color,
|
|
var(--ha-card-background, var(--card-background-color, white))
|
|
);
|
|
}
|
|
:host([wide]) ha-dialog {
|
|
--mdc-dialog-max-width: 90vw;
|
|
}
|
|
:host([fullscreen]) ha-dialog {
|
|
--mdc-dialog-min-width: 100vw;
|
|
--mdc-dialog-max-width: 100vw;
|
|
--mdc-dialog-min-height: 100%;
|
|
--mdc-dialog-max-height: 100%;
|
|
--mdc-shape-medium: 0px;
|
|
--vertial-align-dialog: flex-end;
|
|
--ha-dialog-border-radius: 0px;
|
|
}
|
|
.progress::before {
|
|
content: "";
|
|
position: absolute;
|
|
left: 0;
|
|
width: calc(100% - var(--progress, 60%));
|
|
top: 0;
|
|
height: 2px;
|
|
background: var(--primary-color);
|
|
z-index: 10;
|
|
}
|
|
|
|
ha-header-bar {
|
|
--mdc-theme-on-primary: var(--primary-text-color);
|
|
--mdc-theme-primary: var(--mdc-theme-surface);
|
|
flex-shrink: 0;
|
|
display: block;
|
|
}
|
|
|
|
ha-icon-button > * {
|
|
display: flex;
|
|
}
|
|
.main-title {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
cursor: default;
|
|
}
|
|
.content {
|
|
--padding-x: 24px;
|
|
--padding-y: 20px;
|
|
margin: -20px -24px;
|
|
padding: var(--padding-y) var(--padding-y);
|
|
--header-height: 64px;
|
|
--footer-height: 0px;
|
|
}
|
|
.content:first-child {
|
|
--header-height: 0px;
|
|
}
|
|
|
|
:host([card]) .content {
|
|
--padding-x: 0px;
|
|
--padding-y: 0px;
|
|
--ha-card-box-shadow: none;
|
|
}
|
|
:host([actions]) .content {
|
|
xborder-bottom: 2px solid
|
|
var(--popup-border-color, var(--divider-color));
|
|
--footer-height: 54px;
|
|
}
|
|
:host([wide]) .content {
|
|
width: calc(90vw - 2 * var(--padding-x));
|
|
}
|
|
:host([fullscreen]) .content {
|
|
height: calc(
|
|
100vh - var(--header-height) - var(--footer-height) - 2 *
|
|
var(--padding-y) - 16px
|
|
);
|
|
}
|
|
|
|
.action-button {
|
|
margin-bottom: -24px;
|
|
}
|
|
|
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
|
ha-dialog {
|
|
--mdc-dialog-min-width: 100vw;
|
|
--mdc-dialog-max-width: 100vw;
|
|
--mdc-dialog-min-height: 100%;
|
|
--mdc-dialog-max-height: 100%;
|
|
--mdc-shape-medium: 0px;
|
|
--vertial-align-dialog: flex-end;
|
|
}
|
|
}
|
|
`;
|
|
}
|
|
}
|
|
|
|
if (!customElements.get("browser-mod-popup"))
|
|
customElements.define("browser-mod-popup", BrowserModPopup);
|
|
|
|
export const PopupMixin = (SuperClass) => {
|
|
return class PopupMixinClass extends SuperClass {
|
|
private _popupEl: any;
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
loadLoadCardHelpers();
|
|
|
|
this._popupEl = document.createElement("browser-mod-popup");
|
|
document.body.append(this._popupEl);
|
|
|
|
// const historyListener = async (ev) => {
|
|
// const popupState = ev.state?.browserModPopup;
|
|
// if (popupState) {
|
|
// if (popupState.open) {
|
|
// this._popupEl.setupDialog(...popupState.args);
|
|
// this._popupEl.openDialog();
|
|
// } else {
|
|
// this._popupEl.closeDialog();
|
|
// }
|
|
// }
|
|
// };
|
|
// window.addEventListener("popstate", historyListener);
|
|
}
|
|
|
|
showPopup(...args) {
|
|
// if (history.state?.browserModPopup === undefined) {
|
|
// history.replaceState(
|
|
// {
|
|
// browserModPopup: {
|
|
// open: false,
|
|
// },
|
|
// },
|
|
// ""
|
|
// );
|
|
// }
|
|
// history.pushState(
|
|
// {
|
|
// browserModPopup: {
|
|
// open: true,
|
|
// args,
|
|
// },
|
|
// },
|
|
// ""
|
|
// );
|
|
this._popupEl.setupDialog(...args).then(() => this._popupEl.openDialog());
|
|
}
|
|
|
|
closePopup(...args) {
|
|
this._popupEl.closeDialog();
|
|
this.showMoreInfo("");
|
|
}
|
|
|
|
async showMoreInfo(entityId, large = false, ignore_popup_card = undefined) {
|
|
const base = await hass_base_el();
|
|
base.dispatchEvent(
|
|
new CustomEvent("hass-more-info", {
|
|
bubbles: true,
|
|
composed: true,
|
|
cancelable: false,
|
|
detail: { entityId, ignore_popup_card },
|
|
})
|
|
);
|
|
if (large) {
|
|
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
const dialog: any = base.shadowRoot.querySelector(
|
|
"ha-more-info-dialog"
|
|
);
|
|
if (dialog) dialog.large = true;
|
|
}
|
|
}
|
|
};
|
|
};
|