mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 03:06:41 +00:00
Add basic dialog
This commit is contained in:
parent
e271989cee
commit
868f24eb9f
@ -139,6 +139,7 @@ export class HaDialog extends DialogBase {
|
||||
:host([flexContent]) .mdc-dialog .mdc-dialog__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
scrollbar-color: var(--scrollbar-thumb-color) transparent;
|
||||
}
|
||||
.header_title {
|
||||
display: flex;
|
||||
|
@ -3,7 +3,7 @@ import { getCollection } from "home-assistant-js-websocket";
|
||||
import type { HuiBadge } from "../panels/lovelace/badges/hui-badge";
|
||||
import type { HuiCard } from "../panels/lovelace/cards/hui-card";
|
||||
import type { HuiSection } from "../panels/lovelace/sections/hui-section";
|
||||
import type { Lovelace } from "../panels/lovelace/types";
|
||||
import type { Lovelace, LovelaceDialogSize } from "../panels/lovelace/types";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { LovelaceSectionConfig } from "./lovelace/config/section";
|
||||
import type { LegacyLovelaceConfig } from "./lovelace/config/types";
|
||||
@ -24,6 +24,7 @@ export interface LovelaceViewElement extends HTMLElement {
|
||||
sections?: HuiSection[];
|
||||
isStrategy: boolean;
|
||||
setConfig(config: LovelaceViewConfig): void;
|
||||
getDialogSize?: () => LovelaceDialogSize;
|
||||
}
|
||||
|
||||
export interface LovelaceSectionElement extends HTMLElement {
|
||||
|
@ -45,6 +45,12 @@ export interface CustomActionConfig extends BaseActionConfig {
|
||||
action: "fire-dom-event";
|
||||
}
|
||||
|
||||
export interface OpenDialogActionConfig extends BaseActionConfig {
|
||||
action: "open-dialog";
|
||||
dashboard_path?: string;
|
||||
view_path: string;
|
||||
}
|
||||
|
||||
export interface BaseActionConfig {
|
||||
action: string;
|
||||
confirmation?: ConfirmationRestrictionConfig;
|
||||
@ -60,6 +66,7 @@ export interface RestrictionConfig {
|
||||
}
|
||||
|
||||
export type ActionConfig =
|
||||
| OpenDialogActionConfig
|
||||
| ToggleActionConfig
|
||||
| CallServiceActionConfig
|
||||
| NavigateActionConfig
|
||||
|
@ -7,6 +7,7 @@ import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box
|
||||
import { showVoiceCommandDialog } from "../../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { showViewPopupDialog } from "../views/view-popup/show-view-popup-dialog";
|
||||
import { toggleEntity } from "./entity/toggle-entity";
|
||||
|
||||
declare global {
|
||||
@ -125,6 +126,19 @@ export const handleAction = async (
|
||||
forwardHaptic("failure");
|
||||
}
|
||||
break;
|
||||
case "open-dialog":
|
||||
if (actionConfig.view_path) {
|
||||
showViewPopupDialog(node, {
|
||||
dashboard_path: actionConfig.dashboard_path,
|
||||
view_path: actionConfig.view_path,
|
||||
});
|
||||
} else {
|
||||
showToast(node, {
|
||||
message: "No dashboard path and view path provided",
|
||||
});
|
||||
forwardHaptic("failure");
|
||||
}
|
||||
break;
|
||||
case "url": {
|
||||
if (actionConfig.url_path) {
|
||||
window.open(actionConfig.url_path);
|
||||
|
@ -16,6 +16,7 @@ import type {
|
||||
ActionConfig,
|
||||
CallServiceActionConfig,
|
||||
NavigateActionConfig,
|
||||
OpenDialogActionConfig,
|
||||
UrlActionConfig,
|
||||
} from "../../../data/lovelace/config/action";
|
||||
import type { ServiceAction } from "../../../data/script";
|
||||
@ -30,6 +31,7 @@ const DEFAULT_ACTIONS: UiAction[] = [
|
||||
"toggle",
|
||||
"navigate",
|
||||
"url",
|
||||
"open-dialog",
|
||||
"perform-action",
|
||||
"assist",
|
||||
"none",
|
||||
@ -93,6 +95,16 @@ export class HuiActionEditor extends LitElement {
|
||||
return config?.url_path || "";
|
||||
}
|
||||
|
||||
get _view_path(): string {
|
||||
const config = this.config as OpenDialogActionConfig | undefined;
|
||||
return config?.view_path || "";
|
||||
}
|
||||
|
||||
get _dashboard_path(): string {
|
||||
const config = this.config as OpenDialogActionConfig | undefined;
|
||||
return config?.dashboard_path || "";
|
||||
}
|
||||
|
||||
get _service(): string {
|
||||
const config = this.config as CallServiceActionConfig;
|
||||
return config?.perform_action || config?.service || "";
|
||||
@ -191,6 +203,26 @@ export class HuiActionEditor extends LitElement {
|
||||
></ha-textfield>
|
||||
`
|
||||
: nothing}
|
||||
${this.config?.action === "url"
|
||||
? html`
|
||||
<ha-textfield
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.action-editor.url_path"
|
||||
)}
|
||||
.value=${this._dashboard_path}
|
||||
.configValue=${"dashboard_path"}
|
||||
@input=${this._valueChanged}
|
||||
></ha-textfield>
|
||||
<ha-textfield
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.action-editor.url_path"
|
||||
)}
|
||||
.value=${this._view_path}
|
||||
.configValue=${"view_path"}
|
||||
@input=${this._valueChanged}
|
||||
></ha-textfield>
|
||||
`
|
||||
: nothing}
|
||||
${this.config?.action === "call-service" ||
|
||||
this.config?.action === "perform-action"
|
||||
? html`
|
||||
|
@ -55,6 +55,12 @@ const actionConfigStructNavigate = object({
|
||||
confirmation: optional(actionConfigStructConfirmation),
|
||||
});
|
||||
|
||||
const actionConfigStructOpenDialog = object({
|
||||
action: literal("open-dialog"),
|
||||
dashboard_path: optional(string()),
|
||||
view_path: optional(string()),
|
||||
});
|
||||
|
||||
const actionConfigStructAssist = type({
|
||||
action: literal("assist"),
|
||||
pipeline_id: optional(string()),
|
||||
@ -101,6 +107,9 @@ export const actionConfigStruct = dynamic<any>((value) => {
|
||||
case "more-info": {
|
||||
return actionConfigStructMoreInfo;
|
||||
}
|
||||
case "open-dialog": {
|
||||
return actionConfigStructOpenDialog;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,11 @@ export interface LovelaceGridOptions {
|
||||
max_rows?: number;
|
||||
}
|
||||
|
||||
export interface LovelaceDialogSize {
|
||||
width?: number | "full" | "auto";
|
||||
height?: number | "full" | "auto";
|
||||
}
|
||||
|
||||
export interface LovelaceCard extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
preview?: boolean;
|
||||
|
@ -13,7 +13,7 @@ import type { HuiBadge } from "../badges/hui-badge";
|
||||
import "../badges/hui-view-badges";
|
||||
import type { HuiCard } from "../cards/hui-card";
|
||||
import { computeCardSize } from "../common/compute-card-size";
|
||||
import type { Lovelace } from "../types";
|
||||
import type { Lovelace, LovelaceDialogSize } from "../types";
|
||||
|
||||
// Find column with < 5 size, else smallest column
|
||||
const getColumnIndex = (columnSizes: number[], size: number) => {
|
||||
@ -113,6 +113,14 @@ export class MasonryView extends LitElement implements LovelaceViewElement {
|
||||
});
|
||||
}
|
||||
|
||||
public getDialogSize(): LovelaceDialogSize {
|
||||
this._createColumns();
|
||||
|
||||
return {
|
||||
width: "auto",
|
||||
};
|
||||
}
|
||||
|
||||
private get mqls(): MediaQueryList[] {
|
||||
if (!this._mqls) {
|
||||
this._initMqls();
|
||||
|
@ -11,7 +11,7 @@ import type { HomeAssistant } from "../../../types";
|
||||
import type { HuiCard } from "../cards/hui-card";
|
||||
import type { HuiCardOptions } from "../components/hui-card-options";
|
||||
import type { HuiWarning } from "../components/hui-warning";
|
||||
import type { Lovelace } from "../types";
|
||||
import type { Lovelace, LovelaceDialogSize } from "../types";
|
||||
|
||||
let editCodeLoaded = false;
|
||||
|
||||
@ -28,6 +28,13 @@ export class PanelView extends LitElement implements LovelaceViewElement {
|
||||
|
||||
@state() private _card?: HuiCard | HuiWarning | HuiCardOptions;
|
||||
|
||||
public getDialogSize(): LovelaceDialogSize {
|
||||
return {
|
||||
height: "full",
|
||||
width: "full",
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
public setConfig(_config: LovelaceViewConfig): void {}
|
||||
|
||||
|
@ -43,7 +43,7 @@ import {
|
||||
} from "../editor/lovelace-path";
|
||||
import { showEditSectionDialog } from "../editor/section-editor/show-edit-section-dialog";
|
||||
import type { HuiSection } from "../sections/hui-section";
|
||||
import type { Lovelace } from "../types";
|
||||
import type { Lovelace, LovelaceDialogSize } from "../types";
|
||||
|
||||
export const DEFAULT_MAX_COLUMNS = 4;
|
||||
|
||||
@ -101,6 +101,21 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
public getDialogSize(): LovelaceDialogSize {
|
||||
if (!this._config?.sections) {
|
||||
return {
|
||||
width: 400,
|
||||
};
|
||||
}
|
||||
const size = this._config.sections
|
||||
.map((config) => config.column_span ?? 1)
|
||||
.reduce((acc, val) => acc + val, 0);
|
||||
|
||||
return {
|
||||
width: Math.min(size, 3) * 400,
|
||||
};
|
||||
}
|
||||
|
||||
private _sectionConfigKeys = new WeakMap<HuiSection, string>();
|
||||
|
||||
private _getSectionKey(section: HuiSection) {
|
||||
|
@ -3,7 +3,7 @@ import type { PropertyValues } from "lit";
|
||||
import { ReactiveElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import { fireEvent, type HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/ha-state-label-badge";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { LovelaceViewElement } from "../../../data/lovelace";
|
||||
@ -38,12 +38,13 @@ import { createErrorSectionConfig } from "../sections/hui-error-section";
|
||||
import "../sections/hui-section";
|
||||
import type { HuiSection } from "../sections/hui-section";
|
||||
import { generateLovelaceViewStrategy } from "../strategies/get-strategy";
|
||||
import type { Lovelace } from "../types";
|
||||
import type { Lovelace, LovelaceDialogSize } from "../types";
|
||||
import { getViewType } from "./get-view-type";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"view-updated": undefined;
|
||||
"ll-create-card": { suggested?: string[] } | undefined;
|
||||
"ll-edit-card": { path: LovelaceCardPath };
|
||||
"ll-delete-card": DeleteCardParams;
|
||||
@ -54,6 +55,7 @@ declare global {
|
||||
"ll-delete-badge": DeleteBadgeParams;
|
||||
}
|
||||
interface HTMLElementEventMap {
|
||||
"view-updated": HASSDomEvent<HASSDomEvents["view-updated"]>;
|
||||
"ll-create-card": HASSDomEvent<HASSDomEvents["ll-create-card"]>;
|
||||
"ll-edit-card": HASSDomEvent<HASSDomEvents["ll-edit-card"]>;
|
||||
"ll-delete-card": HASSDomEvent<HASSDomEvents["ll-delete-card"]>;
|
||||
@ -93,6 +95,16 @@ export class HUIView extends ReactiveElement {
|
||||
})
|
||||
protected _clipboard?: LovelaceCardConfig;
|
||||
|
||||
public getDialogSize(): LovelaceDialogSize | undefined {
|
||||
if (!this._layoutElement) {
|
||||
return undefined;
|
||||
}
|
||||
if (this._layoutElement.getDialogSize) {
|
||||
return this._layoutElement.getDialogSize();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private _createCardElement(cardConfig: LovelaceCardConfig) {
|
||||
const element = document.createElement("hui-card");
|
||||
element.hass = this.hass;
|
||||
@ -271,6 +283,14 @@ export class HUIView extends ReactiveElement {
|
||||
|
||||
private _createLayoutElement(config: LovelaceViewConfig): void {
|
||||
this._layoutElement = createViewElement(config) as LovelaceViewElement;
|
||||
this._layoutElement.addEventListener(
|
||||
"ll-upgrade",
|
||||
(ev: Event) => {
|
||||
ev.stopPropagation();
|
||||
fireEvent(this, "view-updated");
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
this._layoutElementType = config.type;
|
||||
this._layoutElement.addEventListener("ll-create-card", (ev) => {
|
||||
showCreateCardDialog(this, {
|
||||
|
206
src/panels/lovelace/views/view-popup/hui-dialog-view-popup.ts
Normal file
206
src/panels/lovelace/views/view-popup/hui-dialog-view-popup.ts
Normal file
@ -0,0 +1,206 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-dialog-header";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
|
||||
import {
|
||||
fetchConfig,
|
||||
isStrategyDashboard,
|
||||
} from "../../../../data/lovelace/config/types";
|
||||
import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
||||
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { generateLovelaceDashboardStrategy } from "../../strategies/get-strategy";
|
||||
import type { Lovelace, LovelaceDialogSize } from "../../types";
|
||||
import "../hui-view";
|
||||
import type { HUIView } from "../hui-view";
|
||||
import type { ViewPopupDialogParams } from "./show-view-popup-dialog";
|
||||
|
||||
@customElement("hui-dialog-view-popup")
|
||||
export class DialogViewPopup
|
||||
extends LitElement
|
||||
implements HassDialog<ViewPopupDialogParams>
|
||||
{
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _params?: ViewPopupDialogParams;
|
||||
|
||||
@state() private _viewIndex?: number;
|
||||
|
||||
@state() private _view?: HUIView;
|
||||
|
||||
@state() private _viewSize?: LovelaceDialogSize;
|
||||
|
||||
@state() private _viewConfig?: LovelaceViewConfig;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("hass") && this._view) {
|
||||
this._view.hass = this.hass;
|
||||
}
|
||||
}
|
||||
|
||||
public showDialog(params: ViewPopupDialogParams): void {
|
||||
this._params = params;
|
||||
this.fetchConfig();
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._params = undefined;
|
||||
this._view = undefined;
|
||||
this._viewConfig = undefined;
|
||||
this._viewSize = undefined;
|
||||
this._viewIndex = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
return true;
|
||||
}
|
||||
|
||||
public async fetchConfig() {
|
||||
if (!this._params) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dashboardPath = this._params.dashboard_path ?? null;
|
||||
|
||||
const rawConfig = await fetchConfig(
|
||||
this.hass.connection,
|
||||
this._params?.dashboard_path ?? null,
|
||||
false
|
||||
);
|
||||
|
||||
let config: LovelaceConfig;
|
||||
|
||||
if (isStrategyDashboard(rawConfig)) {
|
||||
config = await generateLovelaceDashboardStrategy(rawConfig, this.hass);
|
||||
} else {
|
||||
config = rawConfig;
|
||||
}
|
||||
|
||||
const lovelace: Lovelace = {
|
||||
config: config,
|
||||
rawConfig: rawConfig,
|
||||
editMode: false,
|
||||
urlPath: dashboardPath,
|
||||
enableFullEditMode: () => undefined,
|
||||
mode: "storage",
|
||||
locale: this.hass.locale,
|
||||
saveConfig: async () => undefined,
|
||||
deleteConfig: async () => undefined,
|
||||
setEditMode: () => undefined,
|
||||
showToast: () => undefined,
|
||||
};
|
||||
|
||||
this._viewIndex = config.views.findIndex(
|
||||
(v) => v.path === this._params?.view_path
|
||||
);
|
||||
|
||||
const view = document.createElement("hui-view");
|
||||
view.lovelace = lovelace;
|
||||
view.hass = this.hass;
|
||||
view.index = this._viewIndex;
|
||||
this._view = view;
|
||||
this._view.addEventListener(
|
||||
"view-updated",
|
||||
(ev) => {
|
||||
ev.stopPropagation();
|
||||
this._viewSize = this._view?.getDialogSize();
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
this._viewConfig = config.views[this._viewIndex!];
|
||||
await this.updateComplete;
|
||||
|
||||
this._viewSize = this._view.getDialogSize();
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._params || !this._view) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const width = this._viewSize?.width ?? "auto";
|
||||
const height = this._viewSize?.height ?? "auto";
|
||||
const dialogMinWidth =
|
||||
width === "full"
|
||||
? "100vw"
|
||||
: typeof width === "number"
|
||||
? `${width}px`
|
||||
: undefined;
|
||||
const dialogMinHeight =
|
||||
height === "full"
|
||||
? "100vh"
|
||||
: typeof height === "number"
|
||||
? `${height}px`
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${this._viewConfig?.title ?? " "}
|
||||
hideActions
|
||||
flexContent
|
||||
style=${styleMap({
|
||||
"--dialog-width": dialogMinWidth,
|
||||
"--dialog-height": dialogMinHeight,
|
||||
})}
|
||||
>
|
||||
<ha-dialog-header slot="heading">
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
@click=${this.closeDialog}
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<span slot="title">${this._viewConfig?.title}</span>
|
||||
</ha-dialog-header>
|
||||
<div class="content">${this._view}</div>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
--dialog-content-padding: 0;
|
||||
--mdc-dialog-max-width: 90vw;
|
||||
--mdc-dialog-max-height: 90vw;
|
||||
--mdc-dialog-min-width: min(var(--dialog-width, none), 90vw);
|
||||
--mdc-dialog-min-height: min(var(--dialog-height, none), 90vh);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: block;
|
||||
flex: 1 1 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: calc(
|
||||
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
|
||||
);
|
||||
--mdc-dialog-max-width: calc(
|
||||
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
|
||||
);
|
||||
--mdc-dialog-min-height: 100%;
|
||||
--mdc-dialog-max-height: 100%;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-dialog-view-popup": DialogViewPopup;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
export interface ViewPopupDialogParams {
|
||||
dashboard_path?: string;
|
||||
view_path: string;
|
||||
}
|
||||
|
||||
export const showViewPopupDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: ViewPopupDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "hui-dialog-view-popup",
|
||||
dialogImport: () => import("./hui-dialog-view-popup"),
|
||||
dialogParams,
|
||||
});
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user