Compare commits

..

4 Commits

Author SHA1 Message Date
Zack
79593ce937 Few changes 2022-02-21 20:51:50 -06:00
Zack
a0d27a5a83 comment 2022-02-21 20:49:59 -06:00
Zack
1c4f6dc3f1 Way to dismiss 2022-02-21 20:47:41 -06:00
Zack
7d9a60973c Newsletter Addition 2022-02-21 17:31:43 -06:00
179 changed files with 5624 additions and 5050 deletions

631
.yarn/releases/yarn-3.0.2.cjs vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -6,4 +6,4 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.2.0.cjs
yarnPath: .yarn/releases/yarn-3.0.2.cjs

View File

@@ -2,7 +2,7 @@
This is the repository for the official [Home Assistant](https://home-assistant.io) frontend.
[![Screenshot of the frontend](https://raw.githubusercontent.com/home-assistant/frontend/master/docs/screenshot.png)](https://demo.home-assistant.io/)
[![Screenshot of the frontend](https://raw.githubusercontent.com/home-assistant/home-assistant-polymer/master/docs/screenshot.png)](https://demo.home-assistant.io/)
- [View demo of Home Assistant](https://demo.home-assistant.io/)
- [More information about Home Assistant](https://home-assistant.io)

View File

@@ -3,7 +3,7 @@
const gulp = require("gulp");
const fs = require("fs");
const path = require("path");
const { marked } = require("marked");
const marked = require("marked");
const glob = require("glob");
const yaml = require("js-yaml");

View File

@@ -7,7 +7,7 @@ const source = require("vinyl-source-stream");
const vinylBuffer = require("vinyl-buffer");
const gulp = require("gulp");
const fs = require("fs");
const flatmap = require("gulp-flatmap");
const foreach = require("gulp-foreach");
const merge = require("gulp-merge-json");
const rename = require("gulp-rename");
const transform = require("gulp-json-transform");
@@ -183,7 +183,7 @@ gulp.task("build-merged-translations", () =>
})
.pipe(transform((data, file) => lokaliseTransform(data, data, file)))
.pipe(
flatmap((stream, file) => {
foreach((stream, file) => {
// For each language generate a merged json file. It begins with the master
// translation as a failsafe for untranslated strings, and merges all parent
// tags into one file for each specific subtag

View File

@@ -1,5 +1,5 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { LovelaceConfig } from "../../../../src/data/lovelace";
import { Lovelace } from "../../../../src/panels/lovelace/types";
@@ -20,8 +20,6 @@ class HcLovelace extends LitElement {
@property() public urlPath: string | null = null;
@query("hui-view") private _huiView?: HTMLElement;
protected render(): TemplateResult {
const index = this._viewIndex;
if (index === undefined) {
@@ -80,12 +78,12 @@ class HcLovelace extends LitElement {
this.lovelaceConfig.background;
if (configBackground) {
this._huiView!.style.setProperty(
(this.shadowRoot!.querySelector(
"hui-view"
) as HTMLElement)!.style.setProperty(
"--lovelace-background",
configBackground
);
} else {
this._huiView!.style.removeProperty("--lovelace-background");
}
}
}
@@ -118,9 +116,6 @@ class HcLovelace extends LitElement {
:host > * {
flex: 1;
}
hui-view {
background: var(--lovelace-background, var(--primary-background-color));
}
`;
}
}

View File

@@ -14,7 +14,7 @@ import memoizeOne from "memoize-one";
import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
import "../../../src/components/search-input";
import "../../../src/common/search/search-input";
import { extractSearchParam } from "../../../src/common/url/search-params";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-icon-button";

View File

@@ -1,4 +1,5 @@
import "@material/mwc-button";
import "@material/mwc-select";
import "@material/mwc-list/mwc-list-item";
import {
css,
@@ -13,7 +14,6 @@ import { stopPropagation } from "../../../../src/common/dom/stop_propagation";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-alert";
import "../../../../src/components/ha-card";
import "../../../../src/components/ha-select";
import {
HassioAddonDetails,
HassioAddonSetOptionParams,
@@ -57,7 +57,7 @@ class HassioAddonAudio extends LitElement {
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
: ""}
${this._inputDevices &&
html`<ha-select
html`<mwc-select
.label=${this.supervisor.localize(
"addon.configuration.audio.input"
)}
@@ -74,9 +74,9 @@ class HassioAddonAudio extends LitElement {
</mwc-list-item>
`
)}
</ha-select>`}
</mwc-select>`}
${this._outputDevices &&
html`<ha-select
html`<mwc-select
.label=${this.supervisor.localize(
"addon.configuration.audio.output"
)}
@@ -93,7 +93,7 @@ class HassioAddonAudio extends LitElement {
>
`
)}
</ha-select>`}
</mwc-select>`}
</div>
<div class="card-actions">
<ha-progress-button @click=${this._saveSettings}>
@@ -119,10 +119,10 @@ class HassioAddonAudio extends LitElement {
.card-actions {
text-align: right;
}
ha-select {
mwc-select {
width: 100%;
}
ha-select:last-child {
mwc-select:last-child {
margin-top: 8px;
}
`,

View File

@@ -1,11 +1,11 @@
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress";
import "../../../../src/components/ha-markdown";
import "../../../../src/components/ha-select";
import {
extractApiErrorMessage,
ignoreSupervisorError,
@@ -89,7 +89,7 @@ class HassioDatadiskDialog extends LitElement {
)}
<br /><br />
<ha-select
<mwc-select
.label=${this.dialogParams.supervisor.localize(
"dialog.datadisk_move.select_device"
)}
@@ -102,7 +102,7 @@ class HassioDatadiskDialog extends LitElement {
>${device}</mwc-list-item
>`
)}
</ha-select>
</mwc-select>
`
: this.devices === undefined
? this.dialogParams.supervisor.localize(
@@ -161,7 +161,7 @@ class HassioDatadiskDialog extends LitElement {
haStyle,
haStyleDialog,
css`
ha-select {
mwc-select {
width: 100%;
}
ha-circular-progress {

View File

@@ -3,7 +3,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/search-input";
import "../../../../src/common/search/search-input";
import { stringCompare } from "../../../../src/common/string/compare";
import "../../../../src/components/ha-dialog";
import "../../../../src/components/ha-expansion-panel";

View File

@@ -4,7 +4,6 @@ import { customElement, property, state } from "lit/decorators";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-alert";
import "../../../src/components/ha-card";
import "../../../src/components/ha-select";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import { fetchHassioLogs } from "../../../src/data/hassio/supervisor";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
@@ -71,7 +70,7 @@ class HassioSupervisorLog extends LitElement {
: ""}
${this.hass.userData?.showAdvanced
? html`
<ha-select
<mwc-select
.label=${this.supervisor.localize("system.log.log_provider")}
@selected=${this._setLogProvider}
.value=${this._selectedLogProvider}
@@ -83,7 +82,7 @@ class HassioSupervisorLog extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
`
: ""}
@@ -146,7 +145,7 @@ class HassioSupervisorLog extends LitElement {
pre {
white-space: pre-wrap;
}
ha-select {
mwc-select {
width: 100%;
margin-bottom: 4px;
}

View File

@@ -1,8 +1,8 @@
{
"description": "A frontend for Home Assistant",
"description": "A frontend for Home Assistant using the Polymer framework",
"repository": {
"type": "git",
"url": "https://github.com/home-assistant/frontend"
"url": "https://github.com/home-assistant/home-assistant-polymer"
},
"name": "home-assistant-frontend",
"version": "1.0.0",
@@ -46,7 +46,6 @@
"@fullcalendar/daygrid": "5.9.0",
"@fullcalendar/interaction": "5.9.0",
"@fullcalendar/list": "5.9.0",
"@lit-labs/motion": "^1.0.2",
"@lit-labs/virtualizer": "patch:@lit-labs/virtualizer@0.7.0-pre.2#./.yarn/patches/@lit-labs/virtualizer/event-target-shim.patch",
"@material/chips": "14.0.0-canary.261f2db59.0",
"@material/data-table": "14.0.0-canary.261f2db59.0",
@@ -96,7 +95,6 @@
"@vibrant/core": "^3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "^3.2.1-alpha.1",
"@vue/web-component-wrapper": "^1.2.0",
"@webcomponents/scoped-custom-element-registry": "^0.0.5",
"@webcomponents/webcomponentsjs": "^2.2.10",
"app-datepicker": "^5.0.1",
"chart.js": "^3.3.2",
@@ -117,7 +115,7 @@
"leaflet-draw": "^1.0.4",
"lit": "^2.1.2",
"lit-vaadin-helpers": "^0.3.0",
"marked": "^4.0.12",
"marked": "^3.0.2",
"memoize-one": "^5.2.1",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "^0.3.2",
@@ -137,12 +135,12 @@
"vue": "^2.6.12",
"vue2-daterange-picker": "^0.5.1",
"web-animations-js": "^2.3.2",
"workbox-cacheable-response": "^6.4.2",
"workbox-core": "^6.4.2",
"workbox-expiration": "^6.4.2",
"workbox-precaching": "^6.4.2",
"workbox-routing": "^6.4.2",
"workbox-strategies": "^6.4.2",
"workbox-cacheable-response": "^6.1.5",
"workbox-core": "^6.1.5",
"workbox-expiration": "^6.1.5",
"workbox-precaching": "^6.1.5",
"workbox-routing": "^6.1.5",
"workbox-strategies": "^6.1.5",
"xss": "^1.0.9"
},
"devDependencies": {
@@ -171,7 +169,7 @@
"@types/js-yaml": "^4",
"@types/leaflet": "^1",
"@types/leaflet-draw": "^1",
"@types/marked": "^4",
"@types/marked": "^2",
"@types/mocha": "^8",
"@types/qrcode": "^1.4.2",
"@types/sortablejs": "^1",
@@ -198,7 +196,7 @@
"fs-extra": "^7.0.1",
"glob": "^7.2.0",
"gulp": "^4.0.2",
"gulp-flatmap": "^1.0.2",
"gulp-foreach": "^0.1.0",
"gulp-json-transform": "^0.4.6",
"gulp-merge-json": "^1.3.1",
"gulp-rename": "^2.0.0",
@@ -235,7 +233,7 @@
"webpack-dev-server": "^4.3.0",
"webpack-manifest-plugin": "^4.0.2",
"webpackbar": "^5.0.0-3",
"workbox-build": "^6.4.2"
"workbox-build": "^6.1.5"
},
"_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch",
"resolutions": {
@@ -255,6 +253,5 @@
"prettier": {
"trailingComma": "es5",
"arrowParens": "always"
},
"packageManager": "yarn@3.2.0"
}
}

View File

@@ -2,6 +2,6 @@
from pathlib import Path
def where() -> Path:
def where():
"""Return path to the frontend."""
return Path(__file__).parent

View File

View File

@@ -1,6 +1,6 @@
[metadata]
name = home-assistant-frontend
version = 20220226.0
version = 20220220.0
author = The Home Assistant Authors
author_email = hello@home-assistant.io
license = Apache-2.0
@@ -19,8 +19,3 @@ python_requires = >= 3.4.0
[options.packages.find]
include =
hass_frontend*
[mypy]
python_version = 3.4
show_error_codes = True
strict = True

View File

@@ -3,9 +3,9 @@ import type { ForDict } from "../../data/automation";
export const createDurationData = (
duration: string | number | ForDict | undefined
): HaDurationData | undefined => {
): HaDurationData => {
if (duration === undefined) {
return undefined;
return {};
}
if (typeof duration !== "object") {
if (typeof duration === "string" || isNaN(duration)) {

View File

@@ -120,7 +120,6 @@ export const computeOpenIcon = (stateObj: HassEntity): string => {
case "awning":
case "door":
case "gate":
case "curtain":
return mdiArrowExpandHorizontal;
default:
return mdiArrowUp;
@@ -132,7 +131,6 @@ export const computeCloseIcon = (stateObj: HassEntity): string => {
case "awning":
case "door":
case "gate":
case "curtain":
return mdiArrowCollapseHorizontal;
default:
return mdiArrowDown;

View File

@@ -1,12 +1,12 @@
import { mdiClose, mdiMagnify } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "./ha-icon-button";
import "./ha-svg-icon";
import "./ha-textfield";
import type { HaTextField } from "./ha-textfield";
import { HomeAssistant } from "../types";
import { fireEvent } from "../common/dom/fire_event";
import "../../components/ha-icon-button";
import "../../components/ha-svg-icon";
import "../../components/ha-textfield";
import type { HaTextField } from "../../components/ha-textfield";
import { HomeAssistant } from "../../types";
import { fireEvent } from "../dom/fire_event";
@customElement("search-input")
class SearchInput extends LitElement {

View File

@@ -11,7 +11,7 @@ export const debounce = <T extends any[]>(
immediate = false
) => {
let timeout: number | undefined;
const debouncedFunc = (...args: T): void => {
return (...args: T): void => {
const later = () => {
timeout = undefined;
if (!immediate) {
@@ -25,8 +25,4 @@ export const debounce = <T extends any[]>(
func(...args);
}
};
debouncedFunc.cancel = () => {
clearTimeout(timeout);
};
return debouncedFunc;
};

View File

@@ -21,7 +21,7 @@ import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { restoreScroll } from "../../common/decorators/restore-scroll";
import { fireEvent } from "../../common/dom/fire_event";
import "../search-input";
import "../../common/search/search-input";
import { debounce } from "../../common/util/debounce";
import { nextRender } from "../../common/util/render-status";
import { haStyleScrollbar } from "../../resources/styles";

View File

@@ -1,4 +1,5 @@
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select/mwc-select";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
@@ -7,7 +8,6 @@ import {
deviceAutomationsEqual,
} from "../../data/device_automation";
import { HomeAssistant } from "../../types";
import "../ha-select";
const NO_AUTOMATION_KEY = "NO_AUTOMATION";
const UNKNOWN_AUTOMATION_KEY = "UNKNOWN_AUTOMATION";
@@ -90,7 +90,7 @@ export abstract class HaDeviceAutomationPicker<
}
const value = this._value;
return html`
<ha-select
<mwc-select
.label=${this.label}
.value=${value}
@selected=${this._automationChanged}
@@ -113,7 +113,7 @@ export abstract class HaDeviceAutomationPicker<
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
`;
}
@@ -167,7 +167,7 @@ export abstract class HaDeviceAutomationPicker<
static get styles(): CSSResultGroup {
return css`
ha-select {
mwc-select {
width: 100%;
margin-top: 4px;
}

View File

@@ -114,7 +114,7 @@ class HaEntitiesPickerLight extends LitElement {
const newValue = event.detail.value;
if (
newValue === curValue ||
(newValue !== undefined && !isValidEntityId(newValue))
(newValue !== "" && !isValidEntityId(newValue))
) {
return;
}
@@ -147,7 +147,7 @@ class HaEntitiesPickerLight extends LitElement {
}
static override styles = css`
div {
ha-entity-picker {
margin-top: 8px;
}
`;

View File

@@ -1,6 +1,6 @@
import { LitElement, html, TemplateResult, css } from "lit";
import { customElement, property } from "lit/decorators";
import "./ha-select";
import "@material/mwc-select/mwc-select";
import "@material/mwc-list/mwc-list-item";
import "./ha-textfield";
import { fireEvent } from "../common/dom/fire_event";
@@ -193,7 +193,7 @@ export class HaBaseTimeInput extends LitElement {
: ""}
${this.format === 24
? ""
: html`<ha-select
: html`<mwc-select
.required=${this.required}
.value=${this.amPm}
.disabled=${this.disabled}
@@ -205,7 +205,7 @@ export class HaBaseTimeInput extends LitElement {
>
<mwc-list-item value="AM">AM</mwc-list-item>
<mwc-list-item value="PM">PM</mwc-list-item>
</ha-select>`}
</mwc-select>`}
</div>
`;
}
@@ -280,7 +280,7 @@ export class HaBaseTimeInput extends LitElement {
ha-textfield:last-child {
--text-field-border-top-right-radius: var(--mdc-shape-medium);
}
ha-select {
mwc-select {
--mdc-shape-small: 0;
width: 85px;
}

View File

@@ -1,5 +1,5 @@
import "@material/mwc-list/mwc-list-item";
import "./ha-select";
import "@material/mwc-select/mwc-select";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
@@ -24,7 +24,7 @@ class HaBluePrintPicker extends LitElement {
@property({ type: Boolean }) public disabled = false;
public open() {
const select = this.shadowRoot?.querySelector("ha-select");
const select = this.shadowRoot?.querySelector("mwc-select");
if (select) {
// @ts-expect-error
select.menuOpen = true;
@@ -49,7 +49,7 @@ class HaBluePrintPicker extends LitElement {
return html``;
}
return html`
<ha-select
<mwc-select
.label=${this.label ||
this.hass.localize("ui.components.blueprint-picker.label")}
fixedMenuPosition
@@ -71,7 +71,7 @@ class HaBluePrintPicker extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
`;
}
@@ -101,7 +101,7 @@ class HaBluePrintPicker extends LitElement {
:host {
display: inline-block;
}
ha-select {
mwc-select {
width: 100%;
min-width: 200px;
display: block;

View File

@@ -1,5 +1,5 @@
import "@material/mwc-menu";
import type { Corner, Menu, MenuCorner } from "@material/mwc-menu";
import type { Corner, Menu } from "@material/mwc-menu";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
@@ -7,12 +7,6 @@ import { customElement, property, query } from "lit/decorators";
export class HaButtonMenu extends LitElement {
@property() public corner: Corner = "TOP_START";
@property() public menuCorner: MenuCorner = "START";
@property({ type: Number }) public x?: number;
@property({ type: Number }) public y?: number;
@property({ type: Boolean }) public multi = false;
@property({ type: Boolean }) public activatable = false;
@@ -38,12 +32,9 @@ export class HaButtonMenu extends LitElement {
</div>
<mwc-menu
.corner=${this.corner}
.menuCorner=${this.menuCorner}
.fixed=${this.fixed}
.multi=${this.multi}
.activatable=${this.activatable}
.y=${this.y}
.x=${this.x}
>
<slot></slot>
</mwc-menu>

View File

@@ -1,12 +1,5 @@
import "./ha-form";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import type {
HaFormGridSchema,
@@ -33,25 +26,10 @@ export class HaFormGrid extends LitElement implements HaFormElement {
@property() public computeHelper?: (schema: HaFormSchema) => string;
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
protected firstUpdated() {
this.setAttribute("own-margin", "");
}
protected updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (changedProps.has("schema")) {
if (this.schema.column_min_width) {
this.style.setProperty(
"--form-grid-min-width",
this.schema.column_min_width
);
} else {
this.style.setProperty("--form-grid-min-width", "");
}
}
}
protected render(): TemplateResult {
return html`
${this.schema.schema.map(

View File

@@ -1,3 +1,4 @@
import "@material/mwc-select/mwc-select";
import { mdiMenuDown, mdiMenuUp } from "@mdi/js";
import {
css,

View File

@@ -1,14 +1,15 @@
import "@material/mwc-select";
import type { Select } from "@material/mwc-select";
import "@material/mwc-list/mwc-list-item";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation";
import "../ha-radio";
import type { HaRadio } from "../ha-radio";
import "../ha-select";
import type { HaSelect } from "../ha-select";
import { HaFormElement, HaFormSelectData, HaFormSelectSchema } from "./types";
import { stopPropagation } from "../../common/dom/stop_propagation";
import type { HaRadio } from "../ha-radio";
@customElement("ha-form-select")
export class HaFormSelect extends LitElement implements HaFormElement {
@property({ attribute: false }) public schema!: HaFormSelectSchema;
@@ -19,7 +20,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
@property({ type: Boolean }) public disabled = false;
@query("ha-select", true) private _input?: HTMLElement;
@query("mwc-select", true) private _input?: HTMLElement;
public focus() {
if (this._input) {
@@ -49,7 +50,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
}
return html`
<ha-select
<mwc-select
fixedMenuPosition
naturalMenuWidth
.label=${this.label}
@@ -66,13 +67,13 @@ export class HaFormSelect extends LitElement implements HaFormElement {
<mwc-list-item .value=${value}>${label}</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
`;
}
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
let value: string | undefined = (ev.target as HaSelect | HaRadio).value;
let value: string | undefined = (ev.target as Select | HaRadio).value;
if (value === this.data) {
return;
@@ -89,7 +90,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
static get styles(): CSSResultGroup {
return css`
ha-select,
mwc-select,
mwc-formfield {
display: block;
}

View File

@@ -29,7 +29,6 @@ export interface HaFormBaseSchema {
export interface HaFormGridSchema extends HaFormBaseSchema {
type: "grid";
name: "";
column_min_width?: string;
schema: HaFormSchema[];
}

View File

@@ -0,0 +1,145 @@
import "./ha-icon-button";
import "./ha-circular-progress";
import "@material/mwc-button/mwc-button";
import "./ha-card";
import "./ha-textfield";
import { LitElement, TemplateResult, html, CSSResultGroup, css } from "lit";
import { customElement, property, query } from "lit/decorators";
import { mdiClose } from "@mdi/js";
import type { HaTextField } from "./ha-textfield";
import type { HomeAssistant } from "../types";
import { LocalStorage } from "../common/decorators/local-storage";
@customElement("ha-newsletter")
class HaNewsletter extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
@query("ha-textfield")
private _emailField?: HaTextField;
@LocalStorage("dismissNewsletter", true)
private _dismissNewsletter = false;
private _requestStatus?: "inprogress" | "complete";
protected render(): TemplateResult {
if (this._dismissNewsletter) {
return html``;
}
return html`
<ha-card>
<div class="header">
${this.hass.localize("ui.newsletter.newsletter")}
<ha-icon-button
label="Dismiss"
.path=${mdiClose}
@click=${this._dismiss}
></ha-icon-button>
</div>
<div class="newsletter">
${this._requestStatus === "complete"
? html`<span>${this.hass.localize("ui.newsletter.thanks")}</span>`
: html`
<ha-textfield
required
type="email"
.label=${this.hass.localize("ui.newsletter.email")}
.validationMessage=${this.hass.localize(
"ui.newsletter.validation"
)}
></ha-textfield>
${this._requestStatus === "inprogress"
? html`
<ha-circular-progress
active
alt="Loading"
></ha-circular-progress>
`
: html`
<mwc-button
raised
.label=${this.hass.localize("ui.newsletter.subscribe")}
@click=${this._subscribe}
></mwc-button>
`}
`}
</div>
</ha-card>
`;
}
private _subscribe(): void {
if (!this._emailField?.reportValidity()) {
this._emailField!.focus();
return;
}
this._requestStatus = "inprogress";
fetch(
`https://newsletter.home-assistant.io/subscribe?email=${
this._emailField!.value
}`
)
.then(() => {
this._requestStatus = "complete";
setTimeout(this._dismiss, 2000);
})
.catch((err) => {
// Reset request so user can re-enter email
this._requestStatus = undefined;
// eslint-disable-next-line no-console
console.error(err);
});
}
private _dismiss(): void {
this._dismissNewsletter = true;
}
static get styles(): CSSResultGroup {
return css`
.newsletter {
display: flex;
flex-direction: row;
padding: 0 16px 16px;
}
.header {
display: flex;
align-items: center;
justify-content: space-between;
color: var(--ha-card-header-color, --primary-text-color);
font-family: var(--ha-card-header-font-family, inherit);
font-size: var(--ha-card-header-font-size, 24px);
letter-spacing: -0.012em;
line-height: 48px;
padding: 12px 16px 16px;
margin-block-start: 0px;
margin-block-end: 0px;
font-weight: normal;
}
ha-textfield {
flex: 1;
display: block;
padding-right: 8px;
}
mwc-button {
padding-top: 12px;
}
ha-icon-button {
cursor: pointer;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-newsletter": HaNewsletter;
}
}

View File

@@ -1,5 +1,6 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select/mwc-select";
import { mdiCamera } from "@mdi/js";
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";

View File

@@ -1,54 +0,0 @@
import { SelectBase } from "@material/mwc-select/mwc-select-base";
import { styles } from "@material/mwc-select/mwc-select.css";
import { css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { debounce } from "../common/util/debounce";
import { nextRender } from "../common/util/render-status";
@customElement("ha-select")
export class HaSelect extends SelectBase {
// @ts-ignore
@property({ type: Boolean }) public icon?: boolean;
protected override renderLeadingIcon() {
if (!this.icon) {
return nothing;
}
return html`<span class="mdc-select__icon"
><slot name="icon"></slot
></span>`;
}
connectedCallback() {
super.connectedCallback();
window.addEventListener("translations-updated", this._translationsUpdated);
}
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener(
"translations-updated",
this._translationsUpdated
);
}
private _translationsUpdated = debounce(async () => {
await nextRender();
this.layoutOptions();
}, 500);
static override styles = [
styles,
css`
.mdc-select:not(.mdc-select--disabled) .mdc-select__icon {
color: var(--secondary-text-color);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-select": HaSelect;
}
}

View File

@@ -50,12 +50,7 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
private _filterEntities = (entity: HassEntity): boolean => {
if (this.selector.entity?.domain) {
const filterDomain = this.selector.entity.domain;
const entityDomain = computeStateDomain(entity);
if (
(Array.isArray(filterDomain) && !filterDomain.includes(entityDomain)) ||
entityDomain !== filterDomain
) {
if (computeStateDomain(entity) !== this.selector.entity.domain) {
return false;
}
}

View File

@@ -19,24 +19,22 @@ export class HaNumberSelector extends LitElement {
@property() public label?: string;
@property({ type: Boolean }) public required = true;
@property({ type: Boolean }) public disabled = false;
protected render() {
return html`${this.selector.number.mode !== "box"
? html`${this.label}<ha-slider
.min=${this.selector.number.min}
.max=${this.selector.number.max}
.value=${this._value}
.step=${this.selector.number.step ?? 1}
.disabled=${this.disabled}
.required=${this.required}
pin
ignore-bar-touch
@change=${this._handleSliderChange}
>
</ha-slider>`
return html`${this.label}
${this.selector.number.mode !== "box"
? html`<ha-slider
.min=${this.selector.number.min}
.max=${this.selector.number.max}
.value=${this._value}
.step=${this.selector.number.step ?? 1}
.disabled=${this.disabled}
pin
ignore-bar-touch
@change=${this._handleSliderChange}
>
</ha-slider>`
: ""}
<ha-textfield
inputMode="numeric"
@@ -46,10 +44,9 @@ export class HaNumberSelector extends LitElement {
class=${classMap({ single: this.selector.number.mode === "box" })}
.min=${this.selector.number.min}
.max=${this.selector.number.max}
.value=${this.value || ""}
.value=${this.value}
.step=${this.selector.number.step ?? 1}
.disabled=${this.disabled}
.required=${this.required}
.suffix=${this.selector.number.unit_of_measurement}
type="number"
autoValidate
@@ -60,16 +57,14 @@ export class HaNumberSelector extends LitElement {
}
private get _value() {
return this.value ?? (this.selector.number.min || 0);
return this.value ?? 0;
}
private _handleInputChange(ev) {
ev.stopPropagation();
const value =
ev.target.value === "" || isNaN(ev.target.value)
? this.required
? this.selector.number.min || 0
: undefined
? undefined
: Number(ev.target.value);
if (this.value === value) {
return;

View File

@@ -1,11 +1,11 @@
import "@material/mwc-list/mwc-list-item";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation";
import { SelectOption, SelectSelector } from "../../data/selector";
import { HomeAssistant } from "../../types";
import "../ha-select";
import "@material/mwc-select/mwc-select";
import "@material/mwc-list/mwc-list-item";
@customElement("ha-selector-select")
export class HaSelectSelector extends LitElement {
@@ -22,7 +22,7 @@ export class HaSelectSelector extends LitElement {
@property({ type: Boolean }) public disabled = false;
protected render() {
return html`<ha-select
return html`<mwc-select
fixedMenuPosition
naturalMenuWidth
.label=${this.label}
@@ -38,7 +38,7 @@ export class HaSelectSelector extends LitElement {
return html`<mwc-list-item .value=${value}>${label}</mwc-list-item>`;
})}
</ha-select>`;
</mwc-select>`;
}
private _valueChanged(ev) {
@@ -53,7 +53,7 @@ export class HaSelectSelector extends LitElement {
static get styles(): CSSResultGroup {
return css`
ha-select {
mwc-select {
width: 100%;
}
`;

View File

@@ -76,7 +76,6 @@ export class HaTextSelector extends LitElement {
if (value === "" && !this.required) {
value = undefined;
}
fireEvent(this, "value-changed", { value });
}

View File

@@ -53,10 +53,6 @@ export class HaTextField extends TextFieldBase {
padding-right: var(--text-field-suffix-padding-right, 0px);
}
.mdc-text-field__icon {
color: var(--secondary-text-color);
}
input {
text-align: var(--text-field-text-align);
}

View File

@@ -1,337 +0,0 @@
import { animate } from "@lit-labs/motion";
import "@material/mwc-list/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiClose, mdiDelete } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import { fireEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import {
MediaClassBrowserSettings,
MediaPlayerItem,
} from "../../data/media-player";
import {
browseLocalMediaPlayer,
removeLocalMedia,
} from "../../data/media_source";
import { showConfirmationDialog } from "../../dialogs/generic/show-dialog-box";
import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import "../ha-circular-progress";
import "../ha-dialog";
import "../ha-header-bar";
import "../ha-svg-icon";
import "../ha-check-list-item";
import "./ha-media-player-browse";
import "./ha-media-upload-button";
import type { MediaManageDialogParams } from "./show-media-manage-dialog";
@customElement("dialog-media-manage")
class DialogMediaManage extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _currentItem?: MediaPlayerItem;
@state() private _params?: MediaManageDialogParams;
@state() private _uploading = false;
@state() private _deleting = false;
@state() private _selected = new Set<number>();
private _filesChanged = false;
public showDialog(params: MediaManageDialogParams): void {
this._params = params;
this._refreshMedia();
}
public closeDialog() {
if (this._filesChanged && this._params!.onClose) {
this._params!.onClose();
}
this._params = undefined;
this._currentItem = undefined;
this._uploading = false;
this._deleting = false;
this._filesChanged = false;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render(): TemplateResult {
if (!this._params) {
return html``;
}
const children =
this._currentItem?.children?.filter((child) => !child.can_expand) || [];
let fileIndex = 0;
return html`
<ha-dialog
open
scrimClickAction
escapeKeyAction
hideActions
flexContent
.heading=${this._params.currentItem.title}
@closed=${this.closeDialog}
>
<ha-header-bar slot="heading">
${this._selected.size === 0
? html`
<span slot="title">
${this.hass.localize(
"ui.components.media-browser.file_management.title"
)}
</span>
<ha-media-upload-button
.disabled=${this._deleting}
.hass=${this.hass}
.currentItem=${this._params.currentItem}
@uploading=${this._startUploading}
@media-refresh=${this._doneUploading}
slot="actionItems"
></ha-media-upload-button>
${this._uploading
? ""
: html`
<ha-icon-button
.label=${this.hass.localize("ui.dialogs.generic.close")}
.path=${mdiClose}
dialogAction="close"
slot="actionItems"
class="header_button"
dir=${computeRTLDirection(this.hass)}
></ha-icon-button>
`}
`
: html`
<mwc-button
class="danger"
slot="title"
.disabled=${this._deleting}
.label=${this.hass.localize(
`ui.components.media-browser.file_management.${
this._deleting ? "deleting" : "delete"
}`,
{ count: this._selected.size }
)}
@click=${this._handleDelete}
>
<ha-svg-icon .path=${mdiDelete} slot="icon"></ha-svg-icon>
</mwc-button>
${this._deleting
? ""
: html`
<mwc-button
slot="actionItems"
.label=${`Deselect all`}
@click=${this._handleDeselectAll}
>
<ha-svg-icon
.path=${mdiClose}
slot="icon"
></ha-svg-icon>
</mwc-button>
`}
`}
</ha-header-bar>
${!this._currentItem
? html`
<div class="refresh">
<ha-circular-progress active></ha-circular-progress>
</div>
`
: !children.length
? html`<div class="no-items">
<p>
${this.hass.localize(
"ui.components.media-browser.file_management.no_items"
)}
</p>
${this._currentItem?.children?.length
? html`<span class="folders"
>${this.hass.localize(
"ui.components.media-browser.file_management.folders_not_supported"
)}</span
>`
: ""}
</div>`
: html`
<mwc-list multi @selected=${this._handleSelected}>
${repeat(
children,
(item) => item.media_content_id,
(item) => {
const icon = html`
<ha-svg-icon
slot="graphic"
.path=${MediaClassBrowserSettings[
item.media_class === "directory"
? item.children_media_class || item.media_class
: item.media_class
].icon}
></ha-svg-icon>
`;
return html`
<ha-check-list-item
${animate({
id: item.media_content_id,
skipInitial: true,
})}
graphic="icon"
.disabled=${this._uploading || this._deleting}
.selected=${this._selected.has(fileIndex++)}
.item=${item}
>
${icon} ${item.title}
</ha-check-list-item>
`;
}
)}
</mwc-list>
`}
</ha-dialog>
`;
}
private _handleSelected(ev) {
this._selected = ev.detail.index;
}
private _startUploading() {
this._uploading = true;
this._filesChanged = true;
}
private _doneUploading() {
this._uploading = false;
this._refreshMedia();
}
private _handleDeselectAll() {
if (this._selected.size) {
this._selected = new Set();
}
}
private async _handleDelete() {
if (
!(await showConfirmationDialog(this, {
text: this.hass.localize(
"ui.components.media-browser.file_management.confirm_delete",
{ count: this._selected.size }
),
warning: true,
}))
) {
return;
}
this._filesChanged = true;
this._deleting = true;
const toDelete: MediaPlayerItem[] = [];
let fileIndex = 0;
this._currentItem!.children!.forEach((item) => {
if (item.can_expand) {
return;
}
if (this._selected.has(fileIndex++)) {
toDelete.push(item);
}
});
try {
await Promise.all(
toDelete.map(async (item) => {
await removeLocalMedia(this.hass, item.media_content_id);
this._currentItem = {
...this._currentItem!,
children: this._currentItem!.children!.filter((i) => i !== item),
};
})
);
} finally {
this._deleting = false;
this._selected = new Set();
}
}
private async _refreshMedia() {
this._selected = new Set();
this._currentItem = undefined;
this._currentItem = await browseLocalMediaPlayer(
this.hass,
this._params!.currentItem.media_content_id
);
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
ha-dialog {
--dialog-z-index: 8;
--dialog-content-padding: 0;
}
@media (min-width: 800px) {
ha-dialog {
--mdc-dialog-max-width: 800px;
--dialog-surface-position: fixed;
--dialog-surface-top: 40px;
--mdc-dialog-max-height: calc(100vh - 72px);
}
}
ha-header-bar {
--mdc-theme-on-primary: var(--primary-text-color);
--mdc-theme-primary: var(--mdc-theme-surface);
flex-shrink: 0;
border-bottom: 1px solid var(--divider-color, rgba(0, 0, 0, 0.12));
}
ha-media-upload-button,
mwc-button {
--mdc-theme-primary: var(--mdc-theme-on-primary);
}
.danger {
--mdc-theme-primary: var(--error-color);
}
ha-svg-icon[slot="icon"] {
vertical-align: middle;
}
.refresh {
display: flex;
height: 200px;
justify-content: center;
align-items: center;
}
.no-items {
text-align: center;
padding: 16px;
}
.folders {
color: var(--secondary-text-color);
font-style: italic;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-media-manage": DialogMediaManage;
}
}

View File

@@ -1,7 +1,7 @@
import "../ha-header-bar";
import { mdiArrowLeft, mdiClose } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { fireEvent, HASSDomEvent } from "../../common/dom/fire_event";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import type {
@@ -13,11 +13,7 @@ import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import "../ha-dialog";
import "./ha-media-player-browse";
import "./ha-media-manage-button";
import type {
HaMediaPlayerBrowse,
MediaPlayerItemId,
} from "./ha-media-player-browse";
import type { MediaPlayerItemId } from "./ha-media-player-browse";
import { MediaPlayerBrowseDialogParams } from "./show-media-browser-dialog";
@customElement("dialog-media-player-browse")
@@ -30,8 +26,6 @@ class DialogMediaPlayerBrowse extends LitElement {
@state() private _params?: MediaPlayerBrowseDialogParams;
@query("ha-media-player-browse") private _browser!: HaMediaPlayerBrowse;
public showDialog(params: MediaPlayerBrowseDialogParams): void {
this._params = params;
this._navigateIds = params.navigateIds || [
@@ -86,12 +80,6 @@ class DialogMediaPlayerBrowse extends LitElement {
: this._currentItem.title}
</span>
<ha-media-manage-button
slot="actionItems"
.hass=${this.hass}
.currentItem=${this._currentItem}
@media-refresh=${this._refreshMedia}
></ha-media-manage-button>
<ha-icon-button
.label=${this.hass.localize("ui.dialogs.generic.close")}
.path=${mdiClose}
@@ -136,10 +124,6 @@ class DialogMediaPlayerBrowse extends LitElement {
return this._params!.action || "play";
}
private _refreshMedia() {
this._browser.refresh();
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
@@ -173,10 +157,6 @@ class DialogMediaPlayerBrowse extends LitElement {
flex-shrink: 0;
border-bottom: 1px solid var(--divider-color, rgba(0, 0, 0, 0.12));
}
ha-media-manage-button {
--mdc-theme-primary: var(--mdc-theme-on-primary);
}
`,
];
}

View File

@@ -1,10 +1,9 @@
import "@material/mwc-select";
import "@material/mwc-list/mwc-list-item";
import { css, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { LocalStorage } from "../../common/decorators/local-storage";
import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation";
import { fetchCloudStatus, updateCloudPref } from "../../data/cloud";
import {
CloudTTSInfo,
@@ -16,11 +15,12 @@ import {
MediaPlayerBrowseAction,
MediaPlayerItem,
} from "../../data/media-player";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
import { buttonLinkStyle } from "../../resources/styles";
import { HomeAssistant } from "../../types";
import "../ha-select";
import "../ha-textarea";
import { buttonLinkStyle } from "../../resources/styles";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
import { LocalStorage } from "../../common/decorators/local-storage";
import { stopPropagation } from "../../common/dom/stop_propagation";
export interface TtsMediaPickedEvent {
item: MediaPlayerItem;
@@ -103,7 +103,7 @@ class BrowseMediaTTS extends LitElement {
return html`
<div class="cloud-options">
<ha-select
<mwc-select
fixedMenuPosition
naturalMenuWidth
.label=${this.hass.localize(
@@ -117,9 +117,9 @@ class BrowseMediaTTS extends LitElement {
([key, label]) =>
html`<mwc-list-item .value=${key}>${label}</mwc-list-item>`
)}
</ha-select>
</mwc-select>
<ha-select
<mwc-select
fixedMenuPosition
naturalMenuWidth
.label=${this.hass.localize("ui.components.media-browser.tts.gender")}
@@ -131,7 +131,7 @@ class BrowseMediaTTS extends LitElement {
([key, label]) =>
html`<mwc-list-item .value=${key}>${label}</mwc-list-item>`
)}
</ha-select>
</mwc-select>
</div>
`;
}
@@ -256,7 +256,7 @@ class BrowseMediaTTS extends LitElement {
display: flex;
justify-content: space-between;
}
.cloud-options ha-select {
.cloud-options mwc-select {
width: 48%;
}
ha-textarea {

View File

@@ -1,69 +0,0 @@
import { mdiFolderEdit } from "@mdi/js";
import "@material/mwc-button";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { MediaPlayerItem } from "../../data/media-player";
import "../ha-svg-icon";
import { isLocalMediaSourceContentId } from "../../data/media_source";
import type { HomeAssistant } from "../../types";
import { showMediaManageDialog } from "./show-media-manage-dialog";
import { fireEvent } from "../../common/dom/fire_event";
declare global {
interface HASSDomEvents {
"media-refresh": unknown;
}
}
@customElement("ha-media-manage-button")
class MediaManageButton extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() currentItem?: MediaPlayerItem;
@state() _uploading = 0;
protected render(): TemplateResult {
if (
!this.currentItem ||
!isLocalMediaSourceContentId(this.currentItem.media_content_id || "")
) {
return html``;
}
return html`
<mwc-button
.label=${this.hass.localize(
"ui.components.media-browser.file_management.manage"
)}
@click=${this._manage}
>
<ha-svg-icon .path=${mdiFolderEdit} slot="icon"></ha-svg-icon>
</mwc-button>
`;
}
private _manage() {
showMediaManageDialog(this, {
currentItem: this.currentItem!,
onClose: () => fireEvent(this, "media-refresh"),
});
}
static styles = css`
mwc-button {
/* We use icon + text to show disabled state */
--mdc-button-disabled-ink-color: --mdc-theme-primary;
}
ha-svg-icon[slot="icon"],
ha-circular-progress[slot="icon"] {
vertical-align: middle;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-media-manage-button": MediaManageButton;
}
}

View File

@@ -131,11 +131,6 @@ export class HaMediaPlayerBrowse extends LitElement {
currentId.media_content_id,
currentId.media_content_type
);
// Update the parent with latest item.
fireEvent(this, "media-browsed", {
ids: this.navigateIds,
current: this._currentItem,
});
} catch (err) {
this._setError(err);
}
@@ -163,11 +158,10 @@ export class HaMediaPlayerBrowse extends LitElement {
const subtitle = this.hass.localize(
`ui.components.media-browser.class.${currentItem.media_class}`
);
const children = currentItem.children || [];
const mediaClass = MediaClassBrowserSettings[currentItem.media_class];
const childrenMediaClass = currentItem.children_media_class
? MediaClassBrowserSettings[currentItem.children_media_class]
: MediaClassBrowserSettings.directory;
const childrenMediaClass =
MediaClassBrowserSettings[currentItem.children_media_class];
return html`
${
@@ -270,7 +264,7 @@ export class HaMediaPlayerBrowse extends LitElement {
@tts-picked=${this._ttsPicked}
></ha-browse-media-tts>
`
: !children.length && !currentItem.not_shown
: !currentItem.children?.length
? html`
<div class="container no-items">
${currentItem.media_content_id ===
@@ -302,7 +296,7 @@ export class HaMediaPlayerBrowse extends LitElement {
childrenMediaClass.thumbnail_ratio === "portrait",
})}"
>
${children.map(
${currentItem.children.map(
(child) => html`
<div
class="child"
@@ -366,23 +360,11 @@ export class HaMediaPlayerBrowse extends LitElement {
</div>
`
)}
${currentItem.not_shown
? html`
<div class="grid not-shown">
<div class="title">
${this.hass.localize(
"ui.components.media-browser.not_shown",
{ count: currentItem.not_shown }
)}
</div>
</div>
`
: ""}
</div>
`
: html`
<mwc-list>
${children.map(
${currentItem.children.map(
(child) => html`
<mwc-list-item
@click=${this._childClicked}
@@ -426,25 +408,6 @@ export class HaMediaPlayerBrowse extends LitElement {
<li divider role="separator"></li>
`
)}
${currentItem.not_shown
? html`
<mwc-list-item
noninteractive
class="not-shown"
.graphic=${mediaClass.show_list_images
? "medium"
: "avatar"}
dir=${computeRTLDirection(this.hass)}
>
<span class="title">
${this.hass.localize(
"ui.components.media-browser.not_shown",
{ count: currentItem.not_shown }
)}
</span>
</mwc-list-item>
`
: ""}
</mwc-list>
`
}
@@ -911,17 +874,6 @@ export class HaMediaPlayerBrowse extends LitElement {
transition: height 0.5s, margin 0.5s;
}
.not-shown {
font-style: italic;
color: var(--secondary-text-color);
}
.grid.not-shown {
display: flex;
align-items: center;
text-align: center;
}
/* ============= CHILDREN ============= */
mwc-list {

View File

@@ -1,129 +0,0 @@
import { mdiUpload } from "@mdi/js";
import "@material/mwc-button";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { MediaPlayerItem } from "../../data/media-player";
import "../ha-circular-progress";
import "../ha-svg-icon";
import {
isLocalMediaSourceContentId,
uploadLocalMedia,
} from "../../data/media_source";
import type { HomeAssistant } from "../../types";
import { showAlertDialog } from "../../dialogs/generic/show-dialog-box";
declare global {
interface HASSDomEvents {
uploading: unknown;
"media-refresh": unknown;
}
}
@customElement("ha-media-upload-button")
class MediaUploadButton extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() currentItem?: MediaPlayerItem;
@state() _uploading = 0;
protected render(): TemplateResult {
if (
!this.currentItem ||
!isLocalMediaSourceContentId(this.currentItem.media_content_id || "")
) {
return html``;
}
return html`
<mwc-button
.label=${this._uploading > 0
? this.hass.localize(
"ui.components.media-browser.file_management.uploading",
{
count: this._uploading,
}
)
: this.hass.localize(
"ui.components.media-browser.file_management.add_media"
)}
.disabled=${this._uploading > 0}
@click=${this._startUpload}
>
${this._uploading > 0
? html`
<ha-circular-progress
size="tiny"
active
alt=""
slot="icon"
></ha-circular-progress>
`
: html` <ha-svg-icon .path=${mdiUpload} slot="icon"></ha-svg-icon> `}
</mwc-button>
`;
}
private async _startUpload() {
if (this._uploading > 0) {
return;
}
const input = document.createElement("input");
input.type = "file";
input.accept = "audio/*,video/*,image/*";
input.multiple = true;
input.addEventListener(
"change",
async () => {
fireEvent(this, "uploading");
const files = input.files!;
document.body.removeChild(input);
const target = this.currentItem!.media_content_id!;
for (let i = 0; i < files.length; i++) {
this._uploading = files.length - i;
try {
// eslint-disable-next-line no-await-in-loop
await uploadLocalMedia(this.hass, target, files[i]);
} catch (err: any) {
showAlertDialog(this, {
text: this.hass.localize(
"ui.components.media-browser.file_management.upload_failed",
{
reason: err.message || err,
}
),
});
break;
}
}
this._uploading = 0;
fireEvent(this, "media-refresh");
},
{ once: true }
);
// https://stackoverflow.com/questions/47664777/javascript-file-input-onchange-not-working-ios-safari-only
input.style.display = "none";
document.body.append(input);
input.click();
}
static styles = css`
mwc-button {
/* We use icon + text to show disabled state */
--mdc-button-disabled-ink-color: --mdc-theme-primary;
}
ha-svg-icon[slot="icon"],
ha-circular-progress[slot="icon"] {
vertical-align: middle;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-media-upload-button": MediaUploadButton;
}
}

View File

@@ -1,18 +0,0 @@
import { fireEvent } from "../../common/dom/fire_event";
import { MediaPlayerItem } from "../../data/media-player";
export interface MediaManageDialogParams {
currentItem: MediaPlayerItem;
onClose?: () => void;
}
export const showMediaManageDialog = (
element: HTMLElement,
dialogParams: MediaManageDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-media-manage",
dialogImport: () => import("./dialog-media-manage"),
dialogParams,
});
};

View File

@@ -1,4 +1,3 @@
import "@material/mwc-list/mwc-list-item";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { property } from "lit/decorators";
import memoizeOne from "memoize-one";
@@ -6,8 +5,9 @@ import { fireEvent } from "../../common/dom/fire_event";
import { stringCompare } from "../../common/string/compare";
import { fetchUsers, User } from "../../data/user";
import { HomeAssistant } from "../../types";
import "../ha-select";
import "./ha-user-badge";
import "@material/mwc-select/mwc-select";
import "@material/mwc-list/mwc-list-item";
class HaUserPicker extends LitElement {
public hass?: HomeAssistant;
@@ -34,7 +34,7 @@ class HaUserPicker extends LitElement {
protected render(): TemplateResult {
return html`
<ha-select
<mwc-select
.label=${this.label}
.disabled=${this.disabled}
.value=${this.value}
@@ -58,7 +58,7 @@ class HaUserPicker extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
`;
}

View File

@@ -1,24 +0,0 @@
import { HomeAssistant } from "../types";
interface ValidConfig {
valid: true;
error: null;
}
interface InvalidConfig {
valid: false;
error: string;
}
type ValidKeys = "trigger" | "action" | "condition";
export const validateConfig = <
T extends Partial<{ [key in ValidKeys]: unknown }>
>(
hass: HomeAssistant,
config: T
): Promise<Record<keyof T, ValidConfig | InvalidConfig>> =>
hass.callWS({
type: "validate_config",
...config,
});

View File

@@ -168,12 +168,11 @@ export interface MediaPlayerItem {
media_content_type: string;
media_content_id: string;
media_class: string;
children_media_class?: string;
children_media_class: string;
can_play: boolean;
can_expand: boolean;
thumbnail?: string;
children?: MediaPlayerItem[];
not_shown?: number;
}
export const browseMediaPlayer = (
@@ -361,17 +360,3 @@ export const cleanupMediaTitle = (title?: string): string | undefined => {
const index = title.indexOf("?authSig=");
return index > 0 ? title.slice(0, index) : title;
};
/**
* Set volume of a media player entity.
* @param hass Home Assistant object
* @param entity_id entity ID of media player
* @param volume_level number between 0..1
* @returns
*/
export const setMediaPlayerVolume = (
hass: HomeAssistant,
entity_id: string,
volume_level: number
) =>
hass.callService("media_player", "volume_set", { entity_id, volume_level });

View File

@@ -49,12 +49,3 @@ export const uploadLocalMedia = async (
}
return resp.json();
};
export const removeLocalMedia = async (
hass: HomeAssistant,
media_content_id: string
) =>
hass.callWS({
type: "media_source/local_source/remove",
media_content_id,
});

View File

@@ -20,7 +20,7 @@ export type Selector =
export interface EntitySelector {
entity: {
integration?: string;
domain?: string | string[];
domain?: string;
device_class?: string;
};
}
@@ -87,8 +87,8 @@ export interface TargetSelector {
export interface NumberSelector {
number: {
min?: number;
max?: number;
min: number;
max: number;
step?: number;
mode?: "box" | "slider";
unit_of_measurement?: string;

View File

@@ -1,10 +1,7 @@
import { HomeAssistant } from "../types";
import { Action } from "./script";
export const callExecuteScript = (
hass: HomeAssistant,
sequence: Action | Action[]
) =>
export const callExecuteScript = (hass: HomeAssistant, sequence: Action[]) =>
hass.callWS({
type: "execute_script",
sequence,

View File

@@ -15,7 +15,7 @@ import memoizeOne from "memoize-one";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { fireEvent } from "../../common/dom/fire_event";
import { navigate } from "../../common/navigate";
import "../../components/search-input";
import "../../common/search/search-input";
import { caseInsensitiveStringCompare } from "../../common/string/compare";
import { LocalizeFunc } from "../../common/translations/localize";
import "../../components/ha-icon-next";

View File

@@ -1,10 +1,9 @@
import "@material/mwc-button/mwc-button";
import { mdiAlertOutline } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../common/dom/fire_event";
import "../../components/ha-dialog";
import "../../components/ha-svg-icon";
import "../../components/ha-switch";
import "../../components/ha-textfield";
import { haStyleDialog } from "../../resources/styles";
@@ -51,22 +50,20 @@ class DialogBox extends LitElement {
?escapeKeyAction=${confirmPrompt}
@closed=${this._dialogClosed}
defaultAction="ignore"
.heading=${html`${this._params.warning
? html`<ha-svg-icon
.path=${mdiAlertOutline}
style="color: var(--warning-color)"
></ha-svg-icon> `
: ""}${this._params.title
.heading=${this._params.title
? this._params.title
: this._params.confirmation &&
this.hass.localize(
"ui.dialogs.generic.default_confirmation_title"
)}`}
this.hass.localize("ui.dialogs.generic.default_confirmation_title")}
>
<div>
${this._params.text
? html`
<p class=${this._params.prompt ? "no-bottom-padding" : ""}>
<p
class=${classMap({
"no-bottom-padding": Boolean(this._params.prompt),
warning: Boolean(this._params.warning),
})}
>
${this._params.text}
</p>
`
@@ -75,7 +72,7 @@ class DialogBox extends LitElement {
? html`
<ha-textfield
dialogInitialFocus
.value=${this._value || ""}
.value=${this._value}
@keyup=${this._handleKeyUp}
@change=${this._valueChanged}
.label=${this._params.inputLabel
@@ -175,6 +172,9 @@ class DialogBox extends LitElement {
/* Place above other dialogs */
--dialog-z-index: 104;
}
.warning {
color: var(--warning-color);
}
`,
];
}

View File

@@ -0,0 +1,281 @@
import "@material/mwc-button";
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
import "@polymer/paper-input/paper-input";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { fireEvent } from "../../../common/dom/fire_event";
import { FORMAT_NUMBER } from "../../../data/alarm_control_panel";
import LocalizeMixin from "../../../mixins/localize-mixin";
class MoreInfoAlarmControlPanel extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style include="iron-flex"></style>
<style>
paper-input {
margin: auto;
max-width: 200px;
}
.pad {
display: flex;
justify-content: center;
margin-bottom: 24px;
}
.pad div {
display: flex;
flex-direction: column;
}
.pad mwc-button {
padding: 8px;
width: 80px;
}
.actions mwc-button {
flex: 1 0 50%;
margin: 0 4px 16px;
max-width: 200px;
}
mwc-button.disarm {
color: var(--error-color);
}
</style>
<template is="dom-if" if="[[_codeFormat]]">
<paper-input
label="[[localize('ui.card.alarm_control_panel.code')]]"
value="{{_enteredCode}}"
type="password"
inputmode="[[_inputMode]]"
disabled="[[!_inputEnabled]]"
></paper-input>
<template is="dom-if" if="[[_isNumber(_codeFormat)]]">
<div class="pad">
<div>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="1"
outlined
>1</mwc-button
>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="4"
outlined
>4</mwc-button
>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="7"
outlined
>7</mwc-button
>
</div>
<div>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="2"
outlined
>2</mwc-button
>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="5"
outlined
>5</mwc-button
>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="8"
outlined
>8</mwc-button
>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="0"
outlined
>0</mwc-button
>
</div>
<div>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="3"
outlined
>3</mwc-button
>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="6"
outlined
>6</mwc-button
>
<mwc-button
on-click="_digitClicked"
disabled="[[!_inputEnabled]]"
data-digit="9"
outlined
>9</mwc-button
>
<mwc-button
on-click="_clearEnteredCode"
disabled="[[!_inputEnabled]]"
outlined
>
[[localize('ui.card.alarm_control_panel.clear_code')]]
</mwc-button>
</div>
</div>
</template>
</template>
<div class="layout horizontal center-justified actions">
<template is="dom-if" if="[[_disarmVisible]]">
<mwc-button
outlined
class="disarm"
on-click="_callService"
data-service="alarm_disarm"
disabled="[[!_codeValid]]"
>
[[localize('ui.card.alarm_control_panel.disarm')]]
</mwc-button>
</template>
<template is="dom-if" if="[[_armVisible]]">
<mwc-button
outlined
on-click="_callService"
data-service="alarm_arm_home"
disabled="[[!_codeValid]]"
>
[[localize('ui.card.alarm_control_panel.arm_home')]]
</mwc-button>
<mwc-button
outlined
on-click="_callService"
data-service="alarm_arm_away"
disabled="[[!_codeValid]]"
>
[[localize('ui.card.alarm_control_panel.arm_away')]]
</mwc-button>
</template>
</div>
`;
}
static get properties() {
return {
hass: Object,
stateObj: {
type: Object,
observer: "_stateObjChanged",
},
_enteredCode: {
type: String,
value: "",
},
_codeFormat: {
type: String,
value: "",
},
_codeValid: {
type: Boolean,
computed:
"_validateCode(_enteredCode, _codeFormat, _armVisible, _codeArmRequired)",
},
_disarmVisible: {
type: Boolean,
value: false,
},
_armVisible: {
type: Boolean,
value: false,
},
_inputEnabled: {
type: Boolean,
value: false,
},
_inputMode: {
type: String,
computed: "_getInputMode(_codeFormat)",
},
};
}
constructor() {
super();
this._armedStates = [
"armed_home",
"armed_away",
"armed_night",
"armed_custom_bypass",
];
}
_stateObjChanged(newVal, oldVal) {
if (newVal) {
const state = newVal.state;
const props = {
_codeFormat: newVal.attributes.code_format,
_armVisible: state === "disarmed",
_codeArmRequired: newVal.attributes.code_arm_required,
_disarmVisible:
this._armedStates.includes(state) ||
state === "pending" ||
state === "triggered" ||
state === "arming",
};
props._inputEnabled = props._disarmVisible || props._armVisible;
this.setProperties(props);
}
if (oldVal) {
setTimeout(() => {
fireEvent(this, "iron-resize");
}, 500);
}
}
_getInputMode(format) {
return this._isNumber(format) ? "numeric" : "text";
}
_isNumber(format) {
return format === FORMAT_NUMBER;
}
_validateCode(code, format, armVisible, codeArmRequired) {
return !format || code.length > 0 || (armVisible && !codeArmRequired);
}
_digitClicked(ev) {
this._enteredCode += ev.target.getAttribute("data-digit");
}
_clearEnteredCode() {
this._enteredCode = "";
}
_callService(ev) {
const service = ev.target.getAttribute("data-service");
const data = {
entity_id: this.stateObj.entity_id,
code: this._enteredCode,
};
this.hass.callService("alarm_control_panel", service, data).then(() => {
this._enteredCode = "";
});
}
}
customElements.define(
"more-info-alarm_control_panel",
MoreInfoAlarmControlPanel
);

View File

@@ -1,165 +0,0 @@
import "@material/mwc-button";
import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import {
callAlarmAction,
FORMAT_NUMBER,
} from "../../../data/alarm_control_panel";
import type { HomeAssistant } from "../../../types";
const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "clear"];
const ARM_ACTIONS = ["arm_away", "arm_home"];
const DISARM_ACTIONS = ["disarm"];
@customElement("more-info-alarm_control_panel")
export class MoreInfoAlarmControlPanel extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public stateObj?: HassEntity;
@query("#alarmCode") private _input?: HaTextField;
protected render(): TemplateResult {
if (!this.hass || !this.stateObj) {
return html``;
}
return html`
${!this.stateObj.attributes.code_format
? ""
: html`
<div class="center">
<ha-textfield
id="alarmCode"
.label=${this.hass.localize("ui.card.alarm_control_panel.code")}
type="password"
.inputmode=${this.stateObj.attributes.code_format ===
FORMAT_NUMBER
? "numeric"
: "text"}
></ha-textfield>
</div>
`}
${this.stateObj.attributes.code_format !== FORMAT_NUMBER
? ""
: html`
<div id="keypad">
${BUTTONS.map((value) =>
value === ""
? html`<mwc-button disabled></mwc-button>`
: html`
<mwc-button
.value=${value}
@click=${this._handlePadClick}
outlined
class=${classMap({
numberkey: value !== "clear",
})}
>
${value === "clear"
? this.hass!.localize(
`ui.card.alarm_control_panel.clear_code`
)
: value}
</mwc-button>
`
)}
</div>
`}
<div class="actions">
${(this.stateObj.state === "disarmed"
? ARM_ACTIONS
: DISARM_ACTIONS
).map(
(stateAction) => html`
<mwc-button
.action=${stateAction}
@click=${this._handleActionClick}
outlined
>
${this.hass!.localize(
`ui.card.alarm_control_panel.${stateAction}`
)}
</mwc-button>
`
)}
</div>
`;
}
private _handlePadClick(e: MouseEvent): void {
const val = (e.currentTarget! as any).value;
this._input!.value = val === "clear" ? "" : this._input!.value + val;
}
private _handleActionClick(e: MouseEvent): void {
const input = this._input;
callAlarmAction(
this.hass!,
this.stateObj!.entity_id,
(e.currentTarget! as any).action,
input?.value || undefined
);
if (input) {
input.value = "";
}
}
static styles = css`
ha-textfield {
display: block;
margin: 8px;
max-width: 150px;
text-align: center;
}
#keypad {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin: auto;
width: 100%;
max-width: 300px;
}
#keypad mwc-button {
padding: 8px;
width: 30%;
box-sizing: border-box;
}
.actions {
margin: 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.actions mwc-button {
margin: 0 4px 4px;
}
mwc-button#disarm {
color: var(--error-color);
}
mwc-button.numberkey {
--mdc-typography-button-font-size: var(--keypad-font-size, 0.875rem);
}
.center {
display: flex;
justify-content: center;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"more-info-alarm_control_panel": MoreInfoAlarmControlPanel;
}
}

View File

@@ -1,4 +1,3 @@
import "@material/mwc-list/mwc-list-item";
import {
css,
CSSResultGroup,
@@ -10,11 +9,9 @@ import {
import { property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-climate-control";
import "../../../components/ha-select";
import "../../../components/ha-slider";
import "../../../components/ha-switch";
import {
@@ -29,6 +26,9 @@ import {
compareClimateHvacModes,
} from "../../../data/climate";
import { HomeAssistant } from "../../../types";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select/mwc-select";
import { stopPropagation } from "../../../common/dom/stop_propagation";
class MoreInfoClimate extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -168,7 +168,7 @@ class MoreInfoClimate extends LitElement {
<div class="container-hvac_modes">
<div class="controls">
<ha-select
<mwc-select
.label=${hass.localize("ui.card.climate.operation")}
.value=${stateObj.state}
fixedMenuPosition
@@ -186,14 +186,14 @@ class MoreInfoClimate extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
</div>
</div>
${supportPresetMode && stateObj.attributes.preset_modes
? html`
<div class="container-preset_modes">
<ha-select
<mwc-select
.label=${hass.localize("ui.card.climate.preset_mode")}
.value=${stateObj.attributes.preset_mode}
fixedMenuPosition
@@ -210,14 +210,14 @@ class MoreInfoClimate extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
</div>
`
: ""}
${supportFanMode && stateObj.attributes.fan_modes
? html`
<div class="container-fan_list">
<ha-select
<mwc-select
.label=${hass.localize("ui.card.climate.fan_mode")}
.value=${stateObj.attributes.fan_mode}
fixedMenuPosition
@@ -234,14 +234,14 @@ class MoreInfoClimate extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
</div>
`
: ""}
${supportSwingMode && stateObj.attributes.swing_modes
? html`
<div class="container-swing_list">
<ha-select
<mwc-select
.label=${hass.localize("ui.card.climate.swing_mode")}
.value=${stateObj.attributes.swing_mode}
fixedMenuPosition
@@ -254,7 +254,7 @@ class MoreInfoClimate extends LitElement {
<mwc-list-item .value=${mode}>${mode}</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
</div>
`
: ""}
@@ -427,7 +427,7 @@ class MoreInfoClimate extends LitElement {
color: var(--primary-text-color);
}
ha-select {
mwc-select {
width: 100%;
margin-top: 8px;
}

View File

@@ -0,0 +1,148 @@
import "@material/mwc-button";
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
import "@polymer/paper-input/paper-input";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../components/ha-circular-progress";
import "../../../components/ha-markdown";
class MoreInfoConfigurator extends PolymerElement {
static get template() {
return html`
<style include="iron-flex"></style>
<style>
p {
margin: 8px 0;
}
a {
color: var(--primary-color);
}
p > img {
max-width: 100%;
}
p.center {
text-align: center;
}
p.error {
color: #c62828;
}
p.submit {
text-align: center;
height: 41px;
}
ha-circular-progress {
width: 14px;
height: 14px;
margin-right: 20px;
}
[hidden] {
display: none;
}
</style>
<div class="layout vertical">
<template is="dom-if" if="[[isConfigurable]]">
<ha-markdown
breaks
content="[[stateObj.attributes.description]]"
></ha-markdown>
<p class="error" hidden$="[[!stateObj.attributes.errors]]">
[[stateObj.attributes.errors]]
</p>
<template is="dom-repeat" items="[[stateObj.attributes.fields]]">
<paper-input
label="[[item.name]]"
name="[[item.id]]"
type="[[item.type]]"
on-change="fieldChanged"
></paper-input>
</template>
<p class="submit" hidden$="[[!stateObj.attributes.submit_caption]]">
<mwc-button
raised=""
disabled="[[isConfiguring]]"
on-click="submitClicked"
>
<ha-circular-progress
active="[[isConfiguring]]"
hidden="[[!isConfiguring]]"
alt="Configuring"
></ha-circular-progress>
[[stateObj.attributes.submit_caption]]
</mwc-button>
</p>
</template>
</div>
`;
}
static get properties() {
return {
stateObj: {
type: Object,
},
action: {
type: String,
value: "display",
},
isConfigurable: {
type: Boolean,
computed: "computeIsConfigurable(stateObj)",
},
isConfiguring: {
type: Boolean,
value: false,
},
fieldInput: {
type: Object,
value: function () {
return {};
},
},
};
}
computeIsConfigurable(stateObj) {
return stateObj.state === "configure";
}
fieldChanged(ev) {
const el = ev.target;
this.fieldInput[el.name] = el.value;
}
submitClicked() {
const data = {
configure_id: this.stateObj.attributes.configure_id,
fields: this.fieldInput,
};
this.isConfiguring = true;
this.hass.callService("configurator", "configure", data).then(
() => {
this.isConfiguring = false;
},
() => {
this.isConfiguring = false;
}
);
}
}
customElements.define("more-info-configurator", MoreInfoConfigurator);

View File

@@ -1,128 +0,0 @@
import "@material/mwc-button";
import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-alert";
import "../../../components/ha-circular-progress";
import "../../../components/ha-markdown";
import "../../../components/ha-textfield";
import type { HomeAssistant } from "../../../types";
@customElement("more-info-configurator")
export class MoreInfoConfigurator extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public stateObj?: HassEntity;
@state() private _isConfiguring = false;
private _fieldInput = {};
protected render(): TemplateResult {
if (this.stateObj?.state !== "configure") {
return html``;
}
return html`
<div class="container">
<ha-markdown
breaks
.content=${this.stateObj.attributes.description}
></ha-markdown>
${this.stateObj.attributes.errors
? html`<ha-alert alert-type="error">
${this.stateObj.attributes.errors}
</ha-alert>`
: ""}
${this.stateObj.attributes.fields.map(
(field) => html`<ha-textfield
.label=${field.name}
.name=${field.id}
.type=${field.type}
@change=${this._fieldChanged}
></ha-textfield>`
)}
${this.stateObj.attributes.submit_caption
? html`<p class="submit">
<mwc-button
raised
.disabled=${this._isConfiguring}
@click=${this._submitClicked}
>
${this._isConfiguring
? html`<ha-circular-progress
active
alt="Configuring"
></ha-circular-progress>`
: ""}
${this.stateObj.attributes.submit_caption}
</mwc-button>
</p>`
: ""}
</div>
`;
}
private _fieldChanged(ev) {
const el = ev.target;
this._fieldInput[el.name] = el.value;
}
private _submitClicked() {
const data = {
configure_id: this.stateObj!.attributes.configure_id,
fields: this._fieldInput,
};
this._isConfiguring = true;
this.hass.callService("configurator", "configure", data).then(
() => {
this._isConfiguring = false;
},
() => {
this._isConfiguring = false;
}
);
}
static styles = css`
.container {
display: flex;
flex-direction: column;
}
p {
margin: 8px 0;
}
a {
color: var(--primary-color);
}
p > img {
max-width: 100%;
}
p.center {
text-align: center;
}
p.submit {
text-align: center;
height: 41px;
}
ha-circular-progress {
width: 14px;
height: 14px;
margin-right: 20px;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"more-info-configurator": MoreInfoConfigurator;
}
}

View File

@@ -1,4 +1,3 @@
import "@material/mwc-list/mwc-list-item";
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
@@ -9,11 +8,12 @@ import "../../../components/ha-attributes";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button";
import "../../../components/ha-labeled-slider";
import "../../../components/ha-select";
import "../../../components/ha-switch";
import { SUPPORT_SET_SPEED } from "../../../data/fan";
import { EventsMixin } from "../../../mixins/events-mixin";
import LocalizeMixin from "../../../mixins/localize-mixin";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select/mwc-select";
/*
* @appliesMixin EventsMixin
@@ -37,7 +37,7 @@ class MoreInfoFan extends LocalizeMixin(EventsMixin(PolymerElement)) {
display: block;
}
ha-select {
mwc-select {
width: 100%;
}
</style>
@@ -57,7 +57,7 @@ class MoreInfoFan extends LocalizeMixin(EventsMixin(PolymerElement)) {
</div>
<div class="container-preset_modes">
<ha-select
<mwc-select
label="[[localize('ui.card.fan.preset_mode')]]"
value="[[stateObj.attributes.preset_mode]]"
on-selected="presetModeChanged"
@@ -71,7 +71,7 @@ class MoreInfoFan extends LocalizeMixin(EventsMixin(PolymerElement)) {
>
<mwc-list-item value="[[item]]">[[item]]</mwc-list-item>
</template>
</ha-select>
</mwc-select>
</div>
<div class="container-oscillating">

View File

@@ -170,7 +170,11 @@ class MoreInfoHumidifier extends LitElement {
color: var(--primary-text-color);
}
ha-select {
mwc-select {
width: 100%;
}
ha-slider {
width: 100%;
}

View File

@@ -1,4 +1,5 @@
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select/mwc-select";
import { mdiPalette } from "@mdi/js";
import {
css,
@@ -17,7 +18,6 @@ import "../../../components/ha-button-toggle-group";
import "../../../components/ha-color-picker";
import "../../../components/ha-icon-button";
import "../../../components/ha-labeled-slider";
import "../../../components/ha-select";
import {
getLightCurrentModeRgbColor,
LightColorModes,
@@ -208,7 +208,7 @@ class MoreInfoLight extends LitElement {
this.stateObj!.attributes.effect_list?.length
? html`
<hr />
<ha-select
<mwc-select
.label=${this.hass.localize("ui.card.light.effect")}
.value=${this.stateObj.attributes.effect || ""}
fixedMenuPosition
@@ -223,7 +223,7 @@ class MoreInfoLight extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
`
: ""}
`

View File

@@ -0,0 +1,80 @@
import "@material/mwc-button";
import "@polymer/paper-input/paper-input";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../components/ha-attributes";
import LocalizeMixin from "../../../mixins/localize-mixin";
/*
* @appliesMixin LocalizeMixin
*/
class MoreInfoLock extends LocalizeMixin(PolymerElement) {
static get template() {
return html`
<style>
paper-input {
display: inline-block;
}
</style>
<template is="dom-if" if="[[stateObj.attributes.code_format]]">
<paper-input
label="[[localize('ui.card.lock.code')]]"
value="{{enteredCode}}"
pattern="[[stateObj.attributes.code_format]]"
type="password"
></paper-input>
<mwc-button
on-click="callService"
data-service="unlock"
hidden$="[[!isLocked]]"
>[[localize('ui.card.lock.unlock')]]</mwc-button
>
<mwc-button
on-click="callService"
data-service="lock"
hidden$="[[isLocked]]"
>[[localize('ui.card.lock.lock')]]</mwc-button
>
</template>
<ha-attributes
hass="[[hass]]"
state-obj="[[stateObj]]"
extra-filters="code_format"
></ha-attributes>
`;
}
static get properties() {
return {
hass: Object,
stateObj: {
type: Object,
observer: "stateObjChanged",
},
enteredCode: {
type: String,
value: "",
},
isLocked: Boolean,
};
}
stateObjChanged(newVal) {
if (newVal) {
this.isLocked = newVal.state === "locked";
}
}
callService(ev) {
const service = ev.target.getAttribute("data-service");
const data = {
entity_id: this.stateObj.entity_id,
code: this.enteredCode,
};
this.hass.callService("lock", service, data);
}
}
customElements.define("more-info-lock", MoreInfoLock);

View File

@@ -1,70 +0,0 @@
import "@material/mwc-button";
import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import "../../../components/ha-attributes";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import type { HomeAssistant } from "../../../types";
@customElement("more-info-lock")
class MoreInfoLock extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public stateObj?: HassEntity;
@query("ha-textfield") private _textfield?: HaTextField;
protected render(): TemplateResult {
if (!this.hass || !this.stateObj) {
return html``;
}
return html`
${this.stateObj.attributes.code_format
? html`
<ha-textfield
.label=${this.hass.localize("ui.card.lock.code")}
.pattern=${this.stateObj.attributes.code_format}
type="password"
></ha-textfield>
${this.stateObj.state === "locked"
? html`<mwc-button
@click=${this._callService}
data-service="unlock"
>${this.hass.localize("ui.card.lock.unlock")}</mwc-button
>`
: html`<mwc-button @click=${this._callService} data-service="lock"
>${this.hass.localize("ui.card.lock.lock")}</mwc-button
>`}
`
: ""}
<ha-attributes
.hass=${this.hass}
.stateObj=${this.stateObj}
extra-filters="code_format"
></ha-attributes>
`;
}
private _callService(ev) {
const service = ev.target.getAttribute("data-service");
const data = {
entity_id: this.stateObj!.entity_id,
code: this._textfield?.value,
};
this.hass.callService("lock", service, data);
}
static styles = css`
:host {
display: flex;
align-items: center;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"more-info-lock": MoreInfoLock;
}
}

View File

@@ -1,5 +1,6 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select/mwc-select";
import {
mdiLoginVariant,
mdiMusicNote,
@@ -16,7 +17,6 @@ import { stopPropagation } from "../../../common/dom/stop_propagation";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-icon-button";
import "../../../components/ha-select";
import "../../../components/ha-slider";
import "../../../components/ha-svg-icon";
import { showMediaBrowserDialog } from "../../../components/media-player/show-media-browser-dialog";
@@ -50,40 +50,42 @@ class MoreInfoMediaPlayer extends LitElement {
const controls = computeMediaControls(stateObj);
return html`
<div class="controls">
<div class="basic-controls">
${!controls
? ""
: controls.map(
(control) => html`
<ha-icon-button
action=${control.action}
@click=${this._handleClick}
.path=${control.icon}
.label=${this.hass.localize(
`ui.card.media_player.${control.action}`
)}
>
</ha-icon-button>
`
)}
</div>
${supportsFeature(stateObj, SUPPORT_BROWSE_MEDIA)
? html`
<mwc-button
.label=${this.hass.localize(
"ui.card.media_player.browse_media"
${!controls
? ""
: html`
<div class="controls">
<div class="basic-controls">
${controls!.map(
(control) => html`
<ha-icon-button
action=${control.action}
@click=${this._handleClick}
.path=${control.icon}
.label=${this.hass.localize(
`ui.card.media_player.${control.action}`
)}
>
</ha-icon-button>
`
)}
@click=${this._showBrowseMedia}
>
<ha-svg-icon
.path=${mdiPlayBoxMultiple}
slot="icon"
></ha-svg-icon>
</mwc-button>
`
: ""}
</div>
</div>
${supportsFeature(stateObj, SUPPORT_BROWSE_MEDIA)
? html`
<mwc-button
.label=${this.hass.localize(
"ui.card.media_player.browse_media"
)}
@click=${this._showBrowseMedia}
>
<ha-svg-icon
.path=${mdiPlayBoxMultiple}
slot="icon"
></ha-svg-icon>
</mwc-button>
`
: ""}
</div>
`}
${(supportsFeature(stateObj, SUPPORT_VOLUME_SET) ||
supportsFeature(stateObj, SUPPORT_VOLUME_BUTTONS)) &&
![UNAVAILABLE, UNKNOWN, "off"].includes(stateObj.state)
@@ -133,9 +135,12 @@ class MoreInfoMediaPlayer extends LitElement {
stateObj.attributes.source_list?.length
? html`
<div class="source-input">
<ha-select
<ha-svg-icon
class="source-input"
.path=${mdiLoginVariant}
></ha-svg-icon>
<mwc-select
.label=${this.hass.localize("ui.card.media_player.source")}
icon
.value=${stateObj.attributes.source!}
@selected=${this._handleSourceChanged}
fixedMenuPosition
@@ -148,8 +153,7 @@ class MoreInfoMediaPlayer extends LitElement {
<mwc-list-item .value=${source}>${source}</mwc-list-item>
`
)}
<ha-svg-icon .path=${mdiLoginVariant} slot="icon"></ha-svg-icon>
</ha-select>
</mwc-select>
</div>
`
: ""}
@@ -157,10 +161,10 @@ class MoreInfoMediaPlayer extends LitElement {
stateObj.attributes.sound_mode_list?.length
? html`
<div class="sound-input">
<ha-select
<ha-svg-icon .path=${mdiMusicNote}></ha-svg-icon>
<mwc-select
.label=${this.hass.localize("ui.card.media_player.sound_mode")}
.value=${stateObj.attributes.sound_mode!}
icon
fixedMenuPosition
naturalMenuWidth
@selected=${this._handleSoundModeChanged}
@@ -171,8 +175,7 @@ class MoreInfoMediaPlayer extends LitElement {
<mwc-list-item .value=${mode}>${mode}</mwc-list-item>
`
)}
<ha-svg-icon .path=${mdiMusicNote} slot="icon"></ha-svg-icon>
</ha-select>
</mwc-select>
</div>
`
: ""}
@@ -213,8 +216,14 @@ class MoreInfoMediaPlayer extends LitElement {
justify-content: space-between;
}
.source-input ha-select,
.sound-input ha-select {
.source-input ha-svg-icon,
.sound-input ha-svg-icon {
padding: 7px;
margin-top: 24px;
}
.source-input mwc-select,
.sound-input mwc-select {
margin-left: 10px;
flex-grow: 1;
}

View File

@@ -1,4 +1,3 @@
import "@material/mwc-list/mwc-list-item";
import {
mdiFan,
mdiHomeMapMarker,
@@ -16,7 +15,6 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-attributes";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button";
import "../../../components/ha-select";
import { UNAVAILABLE } from "../../../data/entity";
import {
VacuumEntity,
@@ -31,6 +29,8 @@ import {
VACUUM_SUPPORT_STOP,
} from "../../../data/vacuum";
import { HomeAssistant } from "../../../types";
import "@material/mwc-select/mwc-select";
import "@material/mwc-list/mwc-list-item";
interface VacuumCommand {
translationKey: string;
@@ -173,7 +173,7 @@ class MoreInfoVacuum extends LitElement {
? html`
<div>
<div class="flex-horizontal">
<ha-select
<mwc-select
.label=${this.hass!.localize(
"ui.dialogs.more_info_control.vacuum.fan_speed"
)}
@@ -189,7 +189,7 @@ class MoreInfoVacuum extends LitElement {
<mwc-list-item .value=${mode}>${mode}</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
<div
style="justify-content: center; align-self: center; padding-top: 1.3em"
>

View File

@@ -1,4 +1,3 @@
import "@material/mwc-list/mwc-list-item";
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
import { timeOut } from "@polymer/polymer/lib/utils/async";
import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
@@ -7,11 +6,12 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { featureClassNames } from "../../../common/entity/feature_class_names";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-select";
import "../../../components/ha-switch";
import "../../../components/ha-water_heater-control";
import { EventsMixin } from "../../../mixins/events-mixin";
import LocalizeMixin from "../../../mixins/localize-mixin";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select/mwc-select";
/*
* @appliesMixin EventsMixin
@@ -26,7 +26,7 @@ class MoreInfoWaterHeater extends LocalizeMixin(EventsMixin(PolymerElement)) {
color: var(--primary-text-color);
}
ha-select {
mwc-select {
width: 100%;
}
@@ -70,7 +70,7 @@ class MoreInfoWaterHeater extends LocalizeMixin(EventsMixin(PolymerElement)) {
<template is="dom-if" if="[[supportsOperationMode(stateObj)]]">
<div class="container-operation_list">
<div class="controls">
<ha-select
<mwc-select
label="[[localize('ui.card.water_heater.operation')]]"
value="[[stateObj.attributes.operation_mode]]"
on-selected="handleOperationmodeChanged"
@@ -86,7 +86,7 @@ class MoreInfoWaterHeater extends LocalizeMixin(EventsMixin(PolymerElement)) {
[[_localizeOperationMode(localize, item)]]
</mwc-list-item>
</template>
</ha-select>
</mwc-select>
</div>
</div>
</template>

View File

@@ -2,7 +2,6 @@ import {
setPassiveTouchGestures,
setCancelSyntheticClickEvents,
} from "@polymer/polymer/lib/utils/settings";
import "@webcomponents/scoped-custom-element-registry/scoped-custom-element-registry.min";
import "../layouts/home-assistant";
import "../resources/ha-style";
import "../resources/roboto";

View File

@@ -3,7 +3,6 @@ import {
applyThemesOnElement,
invalidateThemeCache,
} from "../common/dom/apply_themes_on_element";
import { fireEvent } from "../common/dom/fire_event";
import { computeLocalize } from "../common/translations/localize";
import { DEFAULT_PANEL } from "../data/panel";
import { NumberFormat, TimeFormat } from "../data/translation";
@@ -86,7 +85,6 @@ export const provideHass = (
hass().updateHass({
localize: await computeLocalize(elements[0], lang, hass().resources),
});
fireEvent(window, "translations-updated");
}
function updateStates(newStates: HassEntities) {

View File

@@ -22,4 +22,4 @@
document.write("<script src='/static/polyfills/webcomponents-bundle.js'><"+"/script>");
}
var isS11_12 = /(?:.*(?:iPhone|iPad).*OS (?:11|12)_\d)|(?:.*Version\/(?:11|12)(?:\.\d+)*.*Safari\/)/.test(navigator.userAgent);
</script>
</script>

View File

@@ -1,10 +1,10 @@
import "@material/mwc-button";
import "@polymer/paper-input/paper-input";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-alert";
import "../../../components/ha-textfield";
import "../../../components/ha-picture-upload";
import type { HaPictureUpload } from "../../../components/ha-picture-upload";
import { AreaRegistryEntryMutableParams } from "../../../data/area_registry";
@@ -69,7 +69,7 @@ class DialogAreaDetail extends LitElement {
>
<div>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
? html` <ha-alert alert-type="error">${this._error}</ha-alert> `
: ""}
<div class="form">
${entry
@@ -83,16 +83,17 @@ class DialogAreaDetail extends LitElement {
`
: ""}
<ha-textfield
<paper-input
.value=${this._name}
@input=${this._nameChanged}
@value-changed=${this._nameChanged}
@keyup=${this._handleKeyup}
.label=${this.hass.localize("ui.panel.config.areas.editor.name")}
.errorMessage=${this.hass.localize(
"ui.panel.config.areas.editor.name_required"
)}
.invalid=${nameInvalid}
dialogInitialFocus
></ha-textfield>
></paper-input>
<ha-picture-upload
.hass=${this.hass}
.value=${this._picture}
@@ -131,9 +132,15 @@ class DialogAreaDetail extends LitElement {
return this._name.trim() !== "";
}
private _nameChanged(ev) {
private _handleKeyup(ev: KeyboardEvent) {
if (ev.keyCode === 13 && this._isNameValid() && !this._submitting) {
this._updateEntry();
}
}
private _nameChanged(ev: PolymerChangedEvent<string>) {
this._error = undefined;
this._name = ev.target.value;
this._name = ev.detail.value;
}
private _pictureChanged(ev: PolymerChangedEvent<string | null>) {
@@ -181,10 +188,6 @@ class DialogAreaDetail extends LitElement {
.form {
padding-bottom: 24px;
}
ha-textfield {
display: block;
margin-bottom: 16px;
}
`,
];
}

View File

@@ -1,5 +1,7 @@
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select";
import type { Select } from "@material/mwc-select";
import { mdiArrowDown, mdiArrowUp, mdiDotsVertical } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators";
@@ -13,19 +15,11 @@ import "../../../../components/ha-alert";
import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-select";
import type { HaSelect } from "../../../../components/ha-select";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import { validateConfig } from "../../../../data/config";
import { Action, getActionType } from "../../../../data/script";
import { callExecuteScript } from "../../../../data/service";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../../dialogs/generic/show-dialog-box";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import { showToast } from "../../../../util/toast";
import "./types/ha-automation-action-activate_scene";
import "./types/ha-automation-action-choose";
import "./types/ha-automation-action-condition";
@@ -186,11 +180,6 @@ export default class HaAutomationActionRow extends LitElement {
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.run_action"
)}
</mwc-list-item>
<mwc-list-item .disabled=${!this._uiModeAvailable}>
${yamlMode
? this.hass.localize(
@@ -252,7 +241,7 @@ export default class HaAutomationActionRow extends LitElement {
></ha-yaml-editor>
`
: html`
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type_select"
)}
@@ -265,7 +254,7 @@ export default class HaAutomationActionRow extends LitElement {
<mwc-list-item .value=${opt}>${label}</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
<div @ui-mode-not-available=${this._handleUiModeNotAvailable}>
${dynamicElement(`ha-automation-action-${type}`, {
@@ -301,54 +290,17 @@ export default class HaAutomationActionRow extends LitElement {
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._runAction();
break;
case 1:
this._switchYamlMode();
break;
case 2:
case 1:
fireEvent(this, "duplicate");
break;
case 3:
case 2:
this._onDelete();
break;
}
}
private async _runAction() {
const validated = await validateConfig(this.hass, {
action: this.action,
});
if (!validated.action.valid) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.automation.editor.actions.invalid_action"
),
text: validated.action.error,
});
return;
}
try {
await callExecuteScript(this.hass, this.action);
} catch (err: any) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.automation.editor.actions.run_action_error"
),
text: err.message || err,
});
return;
}
showToast(this, {
message: this.hass.localize(
"ui.panel.config.automation.editor.actions.run_action_success"
),
});
}
private _onDelete() {
showConfirmationDialog(this, {
text: this.hass.localize(
@@ -363,7 +315,7 @@ export default class HaAutomationActionRow extends LitElement {
}
private _typeChanged(ev: CustomEvent) {
const type = (ev.target as HaSelect).value;
const type = (ev.target as Select).value;
if (!type) {
return;
@@ -423,7 +375,7 @@ export default class HaAutomationActionRow extends LitElement {
.warning ul {
margin: 4px 0;
}
ha-select {
mwc-select {
margin-bottom: 24px;
}
`,

View File

@@ -15,7 +15,7 @@ export class HaDelayAction extends LitElement implements ActionElement {
@property() public action!: DelayAction;
@property() public _timeData?: HaDurationData;
@property() public _timeData!: HaDurationData;
public static get defaultConfig() {
return { delay: "" };

View File

@@ -145,7 +145,7 @@ export class HaDeviceAction extends LitElement {
static styles = css`
ha-device-picker {
display: block;
margin-bottom: 16px;
margin-bottom: 24px;
}
ha-device-action-picker {
display: block;

View File

@@ -50,7 +50,7 @@ export class HaEventAction extends LitElement implements ActionElement {
<ha-yaml-editor
.hass=${this.hass}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.event.event_data"
"ui.panel.config.automation.editor.actions.type.event.service_data"
)}
.name=${"event_data"}
.defaultValue=${event_data}

View File

@@ -35,7 +35,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
const type = getType(action);
return html`
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.repeat.type_select"
)}
@@ -51,7 +51,7 @@ export class HaRepeatAction extends LitElement implements ActionElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
${type === "count"
? html`
<ha-textfield
@@ -162,8 +162,8 @@ export class HaRepeatAction extends LitElement implements ActionElement {
return [
haStyle,
css`
ha-textfield {
margin-top: 16px;
mwc-select {
margin-top: 8px;
}
`,
];

View File

@@ -33,6 +33,7 @@ export class HaWaitForTriggerAction
.value=${timeout || ""}
@change=${this._valueChanged}
></ha-textfield>
<br />
<ha-formfield
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.wait_for_trigger.continue_timeout"

View File

@@ -1,3 +1,5 @@
import "@material/mwc-select";
import type { Select } from "@material/mwc-select";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
@@ -6,8 +8,6 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import { stringCompare } from "../../../../common/string/compare";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-card";
import "../../../../components/ha-select";
import type { HaSelect } from "../../../../components/ha-select";
import "../../../../components/ha-yaml-editor";
import type { Condition } from "../../../../data/automation";
import { haStyle } from "../../../../resources/styles";
@@ -86,7 +86,7 @@ export default class HaAutomationConditionEditor extends LitElement {
></ha-yaml-editor>
`
: html`
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type_select"
)}
@@ -99,7 +99,7 @@ export default class HaAutomationConditionEditor extends LitElement {
<mwc-list-item .value=${opt}>${label}</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
<div>
${dynamicElement(
@@ -112,7 +112,7 @@ export default class HaAutomationConditionEditor extends LitElement {
}
private _typeChanged(ev: CustomEvent) {
const type = (ev.target as HaSelect).value;
const type = (ev.target as Select).value;
if (!type) {
return;
@@ -146,7 +146,7 @@ export default class HaAutomationConditionEditor extends LitElement {
static styles = [
haStyle,
css`
ha-select {
mwc-select {
margin-bottom: 24px;
}
`,

View File

@@ -53,7 +53,7 @@ export class HaStateCondition extends LitElement implements ConditionElement {
protected render() {
const trgFor = createDurationData(this.condition.for);
const data = { ...this.condition, for: trgFor };
const data = { ...this.condition, ...{ for: trgFor } };
const schema = this._schema(this.condition.entity_id);
return html`

View File

@@ -117,8 +117,8 @@ export class HaTimeCondition extends LitElement implements ConditionElement {
);
const data = {
mode_before: inputModeBefore ? "input" : "value",
mode_after: inputModeAfter ? "input" : "value",
mode_before: "value",
mode_after: "value",
...this.condition,
};
@@ -137,11 +137,18 @@ export class HaTimeCondition extends LitElement implements ConditionElement {
ev.stopPropagation();
const newValue = ev.detail.value;
this._inputModeAfter = newValue.mode_after === "input";
this._inputModeBefore = newValue.mode_before === "input";
const newModeAfter = newValue.mode_after === "input";
const newModeBefore = newValue.mode_before === "input";
delete newValue.mode_after;
delete newValue.mode_before;
if (newModeAfter !== this._inputModeAfter) {
this._inputModeAfter = newModeAfter;
newValue.after = undefined;
}
if (newModeBefore !== this._inputModeBefore) {
this._inputModeBefore = newModeBefore;
newValue.before = undefined;
}
Object.keys(newValue).forEach((key) =>
newValue[key] === undefined || newValue[key] === ""

View File

@@ -1,10 +1,10 @@
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { ensureArray } from "../../../../../common/ensure-array";
import "../../../../../components/ha-select";
import type {
AutomationConfig,
Trigger,
@@ -50,7 +50,7 @@ export class HaTriggerCondition extends LitElement {
"ui.panel.config.automation.editor.conditions.type.trigger.no_triggers"
);
}
return html`<ha-select
return html`<mwc-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.trigger.id"
)}
@@ -63,7 +63,7 @@ export class HaTriggerCondition extends LitElement {
<mwc-list-item .value=${trigger.id}> ${trigger.id} </mwc-list-item>
`
)}
</ha-select>`;
</mwc-select>`;
}
private _automationUpdated(config?: AutomationConfig) {

View File

@@ -1,7 +1,7 @@
import "@material/mwc-button/mwc-button";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/entity/ha-entity-toggle";
import "../../../components/ha-card";
@@ -34,8 +34,6 @@ export class HaManualAutomationEditor extends LitElement {
@property() public stateObj?: HassEntity;
@state() private _showDescription = false;
protected render() {
return html`<ha-config-section vertical .isWide=${this.isWide}>
${!this.narrow
@@ -57,30 +55,17 @@ export class HaManualAutomationEditor extends LitElement {
@change=${this._valueChanged}
>
</ha-textfield>
${this._showDescription
? html`
<ha-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.description.label"
)}
.placeholder=${this.hass.localize(
"ui.panel.config.automation.editor.description.placeholder"
)}
name="description"
autogrow
.value=${this.config.description || ""}
@change=${this._valueChanged}
></ha-textarea>
`
: html`
<div class="link-button-row">
<button class="link" @click=${this._addDescription}>
${this.hass.localize(
"ui.panel.config.automation.editor.description.add"
)}
</button>
</div>
`}
<ha-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.description.label"
)}
.placeholder=${this.hass.localize(
"ui.panel.config.automation.editor.description.placeholder"
)}
name="description"
.value=${this.config.description || ""}
@change=${this._valueChanged}
></ha-textarea>
<p>
${this.hass.localize(
"ui.panel.config.automation.editor.modes.description",
@@ -95,11 +80,11 @@ export class HaManualAutomationEditor extends LitElement {
>`
)}
</p>
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.modes.label"
)}
.value=${this.config.mode}
.value=${this.config.mode ? MODES.indexOf(this.config.mode) : 0}
@selected=${this._modeChanged}
fixedMenuPosition
>
@@ -112,10 +97,10 @@ export class HaManualAutomationEditor extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
${this.config.mode && MODES_MAX.includes(this.config.mode)
? html`
<br /><ha-textfield
<ha-textfield
.label=${this.hass.localize(
`ui.panel.config.automation.editor.max.${this.config.mode}`
)}
@@ -123,7 +108,6 @@ export class HaManualAutomationEditor extends LitElement {
name="max"
.value=${this.config.max || "10"}
@change=${this._valueChanged}
class="max"
>
</ha-textfield>
`
@@ -251,17 +235,6 @@ export class HaManualAutomationEditor extends LitElement {
</ha-config-section>`;
}
protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps);
if (
!this._showDescription &&
changedProps.has("config") &&
this.config.description
) {
this._showDescription = true;
}
}
private _runActions(ev: Event) {
triggerAutomationActions(this.hass, (ev.target as any).stateObj.entity_id);
}
@@ -332,10 +305,6 @@ export class HaManualAutomationEditor extends LitElement {
});
}
private _addDescription() {
this._showDescription = true;
}
static get styles(): CSSResultGroup {
return [
haStyle,
@@ -343,13 +312,6 @@ export class HaManualAutomationEditor extends LitElement {
ha-card {
overflow: hidden;
}
.link-button-row {
padding: 14px;
}
ha-textarea,
ha-textfield {
display: block;
}
span[slot="introduction"] a {
color: var(--primary-color);
}
@@ -359,10 +321,8 @@ export class HaManualAutomationEditor extends LitElement {
ha-entity-toggle {
margin-right: 8px;
}
ha-select,
.max {
margin-top: 16px;
width: 200px;
mwc-select {
margin-top: 8px;
}
`,
];

View File

@@ -1,26 +1,22 @@
import { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, PropertyValues } from "lit";
import "@material/mwc-select";
import type { Select } from "@material/mwc-select";
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stringCompare } from "../../../../common/string/compare";
import { handleStructError } from "../../../../common/structs/handle-errors";
import { LocalizeFunc } from "../../../../common/translations/localize";
import { debounce } from "../../../../common/util/debounce";
import "../../../../components/ha-alert";
import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-select";
import type { HaSelect } from "../../../../components/ha-select";
import "../../../../components/ha-alert";
import "../../../../components/ha-textfield";
import { subscribeTrigger, Trigger } from "../../../../data/automation";
import { validateConfig } from "../../../../data/config";
import "../../../../components/ha-icon-button";
import type { Trigger } from "../../../../data/automation";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
@@ -94,12 +90,6 @@ export default class HaAutomationTriggerRow extends LitElement {
@state() private _requestShowId = false;
@state() private _triggered = false;
@state() private _triggerColor = false;
private _triggerUnsub?: Promise<UnsubscribeFunc>;
private _processedTypes = memoizeOne(
(localize: LocalizeFunc): [string, string][] =>
OPTIONS.map(
@@ -194,7 +184,7 @@ export default class HaAutomationTriggerRow extends LitElement {
></ha-yaml-editor>
`
: html`
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type_select"
)}
@@ -207,7 +197,7 @@ export default class HaAutomationTriggerRow extends LitElement {
<mwc-list-item .value=${opt}>${label}</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
${showId
? html`
@@ -229,98 +219,10 @@ export default class HaAutomationTriggerRow extends LitElement {
</div>
`}
</div>
<div
class="triggered ${classMap({
active: this._triggered,
accent: this._triggerColor,
})}"
>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.triggered"
)}
</div>
</ha-card>
`;
}
protected override updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (changedProps.has("trigger")) {
this._subscribeTrigger();
}
}
public connectedCallback(): void {
super.connectedCallback();
if (this.hasUpdated && this.trigger) {
this._subscribeTrigger();
}
}
public disconnectedCallback(): void {
super.disconnectedCallback();
if (this._triggerUnsub) {
this._triggerUnsub.then((unsub) => unsub());
this._triggerUnsub = undefined;
}
this._doSubscribeTrigger.cancel();
}
private _subscribeTrigger() {
// Clean up old trigger subscription.
if (this._triggerUnsub) {
this._triggerUnsub.then((unsub) => unsub());
this._triggerUnsub = undefined;
}
this._doSubscribeTrigger();
}
private _doSubscribeTrigger = debounce(async () => {
let untriggerTimeout: number | undefined;
const showTriggeredTime = 5000;
const trigger = this.trigger;
// Clean up old trigger subscription.
if (this._triggerUnsub) {
this._triggerUnsub.then((unsub) => unsub());
this._triggerUnsub = undefined;
}
const validateResult = await validateConfig(this.hass, {
trigger: this.trigger,
});
// Don't do anything if trigger not valid or if trigger changed.
if (!validateResult.trigger.valid || this.trigger !== trigger) {
return;
}
const triggerUnsub = subscribeTrigger(
this.hass,
() => {
if (untriggerTimeout !== undefined) {
clearTimeout(untriggerTimeout);
this._triggerColor = !this._triggerColor;
} else {
this._triggerColor = false;
}
this._triggered = true;
untriggerTimeout = window.setTimeout(() => {
this._triggered = false;
untriggerTimeout = undefined;
}, showTriggeredTime);
},
trigger
);
triggerUnsub.catch(() => {
if (this._triggerUnsub === triggerUnsub) {
this._triggerUnsub = undefined;
}
});
this._triggerUnsub = triggerUnsub;
}, 5000);
private _handleUiModeNotAvailable(ev: CustomEvent) {
this._warnings = handleStructError(this.hass, ev.detail).warnings;
if (!this._yamlMode) {
@@ -359,7 +261,7 @@ export default class HaAutomationTriggerRow extends LitElement {
}
private _typeChanged(ev: CustomEvent) {
const type = (ev.target as HaSelect).value;
const type = (ev.target as Select).value;
if (!type) {
return;
@@ -425,38 +327,13 @@ export default class HaAutomationTriggerRow extends LitElement {
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
.triggered {
position: absolute;
top: 0px;
right: 0px;
left: 0px;
text-transform: uppercase;
pointer-events: none;
font-weight: bold;
font-size: 14px;
background-color: var(--primary-color);
color: var(--text-primary-color);
max-height: 0px;
overflow: hidden;
transition: max-height 0.3s;
text-align: center;
border-top-right-radius: var(--ha-card-border-radius, 4px);
border-top-left-radius: var(--ha-card-border-radius, 4px);
}
.triggered.active {
max-height: 100px;
}
.triggered.accent {
background-color: var(--accent-color);
color: var(--text-accent-color, var(--text-primary-color));
}
.rtl .card-menu {
float: left;
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
ha-select {
mwc-select {
margin-bottom: 24px;
}
ha-textfield {

View File

@@ -1,9 +1,9 @@
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select";
import { html, LitElement, PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { caseInsensitiveStringCompare } from "../../../../../common/string/compare";
import "../../../../../components/ha-select";
import { TagTrigger } from "../../../../../data/automation";
import { fetchTags, Tag } from "../../../../../data/tag";
import { HomeAssistant } from "../../../../../types";
@@ -29,7 +29,7 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
protected render() {
const { tag_id } = this.trigger;
return html`
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.tag.label"
)}
@@ -44,7 +44,7 @@ export class HaTagTrigger extends LitElement implements TriggerElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
`;
}

View File

@@ -23,9 +23,9 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
private _schema = memoizeOne(
(localize: LocalizeFunc, inputMode?: boolean): HaFormSchema[] => {
const atSelector = inputMode
? { entity: { domain: "input_datetime" } }
: { time: {} };
const modeSchema = inputMode
? { name: "at", selector: { entity: { domain: "input_datetime" } } }
: { name: "at", selector: { time: {} } };
return [
{
@@ -47,7 +47,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
],
],
},
{ name: "at", selector: atSelector },
modeSchema,
];
}
);
@@ -80,7 +80,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
const schema: HaFormSchema[] = this._schema(this.hass.localize, inputMode);
const data = {
mode: inputMode ? "input" : "value",
mode: "value",
...this.trigger,
};
@@ -99,8 +99,7 @@ export class HaTimeTrigger extends LitElement implements TriggerElement {
ev.stopPropagation();
const newValue = ev.detail.value;
this._inputMode = newValue.mode === "input";
delete newValue.mode;
this._inputMode = newValue.mode.value === "input";
Object.keys(newValue).forEach((key) =>
newValue[key] === undefined || newValue[key] === ""

View File

@@ -1,13 +1,13 @@
import "@material/mwc-button";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import "@polymer/paper-input/paper-input";
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state, query } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-circular-progress";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-expansion-panel";
import "../../../components/ha-markdown";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
import {
BlueprintImportResult,
importBlueprint,
@@ -32,7 +32,7 @@ class DialogImportBlueprint extends LitElement {
@state() private _url?: string;
@query("#input") private _input?: HaTextField;
@query("#input") private _input?: PaperInputElement;
public showDialog(params): void {
this._params = params;
@@ -90,13 +90,13 @@ class DialogImportBlueprint extends LitElement {
</ul>
`
: html`
<ha-textfield
<paper-input
id="input"
.value=${this._result.suggested_filename || ""}
.value=${this._result.suggested_filename}
.label=${this.hass.localize(
"ui.panel.config.blueprint.add.file_name"
)}
></ha-textfield>
></paper-input>
`}
<ha-expansion-panel
.header=${this.hass.localize(
@@ -116,14 +116,14 @@ class DialogImportBlueprint extends LitElement {
"ui.panel.config.blueprint.add.community_forums"
)}</a
>`
)}<ha-textfield
)}<paper-input
id="input"
.label=${this.hass.localize(
"ui.panel.config.blueprint.add.url"
)}
.value=${this._url || ""}
.value=${this._url}
dialogInitialFocus
></ha-textfield>`}
></paper-input>`}
</div>
${!this._result
? html`<mwc-button
@@ -212,15 +212,9 @@ class DialogImportBlueprint extends LitElement {
}
}
static styles = [
haStyleDialog,
css`
ha-textfield {
display: block;
margin-top: 8px;
}
`,
];
static get styles(): CSSResultGroup {
return haStyleDialog;
}
}
declare global {

View File

@@ -29,7 +29,6 @@ import "./cloud-remote-pref";
import "./cloud-tts-pref";
import "./cloud-webhooks";
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
@customElement("cloud-account")
export class CloudAccount extends SubscribeMixin(LitElement) {
@@ -277,22 +276,11 @@ export class CloudAccount extends SubscribeMixin(LitElement) {
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
showConfirmationDialog(this, {
text: this.hass.localize(
"ui.panel.config.cloud.account.sign_out_confirm"
),
confirmText: this.hass!.localize("ui.common.yes"),
dismissText: this.hass!.localize("ui.common.no"),
confirm: () => this._logoutFromCloud(),
});
await cloudLogout(this.hass);
fireEvent(this, "ha-refresh-cloud-status");
}
}
private async _logoutFromCloud() {
await cloudLogout(this.hass);
fireEvent(this, "ha-refresh-cloud-status");
}
_computeRTLDirection(hass) {
return computeRTLDirection(hass);
}

View File

@@ -1,11 +1,11 @@
import "@material/mwc-button";
import "@material/mwc-select";
import "@material/mwc-list/mwc-list-item";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-card";
import "../../../../components/ha-select";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-switch";
import { CloudStatusLoggedIn, updateCloudPref } from "../../../../data/cloud";
@@ -54,7 +54,7 @@ export class CloudTTSPref extends LitElement {
)}
<br /><br />
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.cloud.account.tts.default_language"
)}
@@ -66,9 +66,9 @@ export class CloudTTSPref extends LitElement {
([key, label]) =>
html`<mwc-list-item .value=${key}>${label}</mwc-list-item>`
)}
</ha-select>
</mwc-select>
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.cloud.account.tts.default_gender"
)}
@@ -80,7 +80,7 @@ export class CloudTTSPref extends LitElement {
([key, label]) =>
html`<mwc-list-item .value=${key}>${label}</mwc-list-item>`
)}
</ha-select>
</mwc-select>
</div>
<div class="card-actions">
<mwc-button @click=${this._openTryDialog}>

View File

@@ -1,8 +1,9 @@
import "@material/mwc-button";
import "@material/mwc-select";
import "@material/mwc-list/mwc-list-item";
import { mdiPlayCircleOutline, mdiRobot } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { customElement, property, state, query } from "lit/decorators";
import { LocalStorage } from "../../../../common/decorators/local-storage";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
@@ -10,9 +11,8 @@ import { computeStateDomain } from "../../../../common/entity/compute_state_doma
import { computeStateName } from "../../../../common/entity/compute_state_name";
import { supportsFeature } from "../../../../common/entity/supports-feature";
import { createCloseHeading } from "../../../../components/ha-dialog";
import "../../../../components/ha-select";
import "../../../../components/ha-textarea";
import type { HaTextArea } from "../../../../components/ha-textarea";
import "../../../../components/ha-textarea";
import { showAutomationEditor } from "../../../../data/automation";
import { SUPPORT_PLAY_MEDIA } from "../../../../data/media-player";
import { convertTextToSpeech } from "../../../../data/tts";
@@ -74,7 +74,7 @@ export class DialogTryTts extends LitElement {
>
</ha-textarea>
<ha-select
<mwc-select
.label=${this.hass.localize(
"ui.panel.config.cloud.account.tts.dialog.target"
)}
@@ -103,7 +103,7 @@ export class DialogTryTts extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
</div>
<mwc-button
slot="primaryAction"
@@ -215,10 +215,10 @@ export class DialogTryTts extends LitElement {
--mdc-dialog-max-width: 500px;
}
ha-textarea,
ha-select {
mwc-select {
width: 100%;
}
ha-select {
mwc-select {
margin-top: 8px;
}
`,

View File

@@ -1,11 +1,12 @@
import "@material/mwc-button/mwc-button";
import "@polymer/paper-input/paper-input";
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
import { css, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-card";
import { ConfigUpdateValues, saveCoreConfig } from "../../../data/core";
import type { PolymerChangedEvent } from "../../../polymer-types";
import type { HomeAssistant } from "../../../types";
import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield";
@customElement("ha-config-name-form")
class ConfigNameForm extends LitElement {
@@ -33,15 +34,16 @@ class ConfigNameForm extends LitElement {
</p>
`
: ""}
<ha-textfield
<paper-input
class="flex"
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.location_name"
)}
name="name"
.disabled=${disabled}
.value=${this._nameValue}
@change=${this._handleChange}
></ha-textfield>
@value-changed=${this._handleChange}
></paper-input>
</div>
<div class="card-actions">
<mwc-button @click=${this._save} .disabled=${disabled}>
@@ -60,9 +62,9 @@ class ConfigNameForm extends LitElement {
: this.hass.config.location_name;
}
private _handleChange(ev) {
const target = ev.currentTarget as HaTextField;
this._name = target.value;
private _handleChange(ev: PolymerChangedEvent<string>) {
const target = ev.currentTarget as PaperInputElement;
this[`_${target.name}`] = target.value;
}
private async _save() {
@@ -83,9 +85,6 @@ class ConfigNameForm extends LitElement {
.card-actions {
text-align: right;
}
ha-textfield {
display: block;
}
`;
}
}

View File

@@ -1,10 +1,5 @@
import {
mdiCloudLock,
mdiDotsVertical,
mdiLightbulbOutline,
mdiMagnify,
mdiNewBox,
} from "@mdi/js";
import "../../../components/ha-newsletter";
import { mdiCloudLock, mdiDotsVertical, mdiMagnify } from "@mdi/js";
import "@material/mwc-list/mwc-list-item";
import type { ActionDetail } from "@material/mwc-list";
import "@polymer/app-layout/app-header/app-header";
@@ -17,14 +12,13 @@ import {
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement, property } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../components/ha-card";
import "../../../components/ha-icon-next";
import "../../../components/ha-icon-button";
import "../../../components/ha-menu-button";
import "../../../components/ha-button-menu";
import "../../../components/ha-svg-icon";
import { CloudStatus } from "../../../data/cloud";
import {
refreshSupervisorAvailableUpdates,
@@ -41,66 +35,6 @@ import "./ha-config-updates";
import { fireEvent } from "../../../common/dom/fire_event";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import { showToast } from "../../../util/toast";
import { documentationUrl } from "../../../util/documentation-url";
const randomTip = (hass: HomeAssistant) => {
const weighted: string[] = [];
const tips = [
{
content: hass.localize(
"ui.panel.config.tips.join",
"forums",
html`<a
href="https://community.home-assistant.io"
target="_blank"
rel="noreferrer"
>Forums</a
>`,
"twitter",
html`<a
href=${documentationUrl(hass, `/twitter`)}
target="_blank"
rel="noreferrer"
>Twitter</a
>`,
"discord",
html`<a
href=${documentationUrl(hass, `/join-chat`)}
target="_blank"
rel="noreferrer"
>Chat</a
>`,
"blog",
html`<a
href=${documentationUrl(hass, `/blog`)}
target="_blank"
rel="noreferrer"
>Blog</a
>`,
"newsletter",
html`<span class="keep-together"
><a
href=${documentationUrl(hass, `/newsletter`)}
target="_blank"
rel="noreferrer"
>Newsletter</a
>
<ha-svg-icon class="new" .path=${mdiNewBox}></ha-svg-icon
></span>`
),
weight: 2,
},
{ content: hass.localize("ui.dialogs.quick-bar.key_c_hint"), weight: 1 },
];
tips.forEach((tip) => {
for (let i = 0; i < tip.weight; i++) {
weighted.push(tip.content);
}
});
return weighted[Math.floor(Math.random() * weighted.length)];
};
@customElement("ha-config-dashboard")
class HaConfigDashboard extends LitElement {
@@ -118,8 +52,6 @@ class HaConfigDashboard extends LitElement {
@property() public showAdvanced!: boolean;
@state() private _tip?: string;
private _notifyUpdates = false;
protected render(): TemplateResult {
@@ -203,11 +135,7 @@ class HaConfigDashboard extends LitElement {
.pages=${configSections.dashboard}
></ha-config-navigation>
</ha-card>`}
<div class="tips">
<ha-svg-icon .path=${mdiLightbulbOutline}></ha-svg-icon>
<span class="tip-word">Tip!</span>
<span class="text">${this._tip}</span>
</div>
<ha-newsletter .hass=${this.hass}></ha-newsletter>
</ha-config-section>
</ha-app-layout>
`;
@@ -216,10 +144,6 @@ class HaConfigDashboard extends LitElement {
protected override updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (!this._tip && changedProps.has("hass")) {
this._tip = randomTip(this.hass);
}
if (!changedProps.has("supervisorUpdates") || !this._notifyUpdates) {
return;
}
@@ -301,27 +225,6 @@ class HaConfigDashboard extends LitElement {
:host([narrow]) ha-config-section {
margin-top: -42px;
}
.tips {
text-align: center;
margin-bottom: max(env(safe-area-inset-bottom), 8px);
}
.tips .text {
color: var(--secondary-text-color);
}
.tip-word {
font-weight: 500;
}
.new {
color: var(--primary-color);
}
.keep-together {
display: inline-block;
}
`,
];
}

View File

@@ -1,12 +1,13 @@
import "@material/mwc-button/mwc-button";
import "@polymer/paper-input/paper-input";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-area-picker";
import "../../../../components/ha-dialog";
import type { HaSwitch } from "../../../../components/ha-switch";
import "../../../../components/ha-textfield";
import { computeDeviceName } from "../../../../data/device_registry";
import { PolymerChangedEvent } from "../../../../polymer-types";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail";
@@ -56,18 +57,16 @@ class DialogDeviceRegistryDetail extends LitElement {
.heading=${computeDeviceName(device, this.hass)}
>
<div>
${this._error
? html`<ha-alert alert-type="error">${this._error}</ha-alert> `
: ""}
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
<div class="form">
<ha-textfield
<paper-input
.value=${this._nameByUser}
@input=${this._nameChanged}
@value-changed=${this._nameChanged}
.label=${this.hass.localize("ui.panel.config.devices.name")}
.placeholder=${device.name || ""}
.disabled=${this._submitting}
dialogInitialFocus
></ha-textfield>
></paper-input>
<ha-area-picker
.hass=${this.hass}
.value=${this._areaId}
@@ -133,9 +132,9 @@ class DialogDeviceRegistryDetail extends LitElement {
`;
}
private _nameChanged(ev): void {
private _nameChanged(ev: PolymerChangedEvent<string>): void {
this._error = undefined;
this._nameByUser = ev.target.value;
this._nameByUser = ev.detail.value;
}
private _areaPicked(event: CustomEvent): void {
@@ -175,9 +174,8 @@ class DialogDeviceRegistryDetail extends LitElement {
mwc-button.warning {
margin-right: auto;
}
ha-textfield {
display: block;
margin-bottom: 16px;
.error {
color: var(--error-color);
}
ha-switch {
margin-right: 16px;

View File

@@ -438,13 +438,6 @@ export class HaConfigDeviceDashboard extends LitElement {
)}
.path=${mdiFilterVariant}
></ha-icon-button>
${this.narrow && activeFilters?.length
? html`<mwc-list-item @click=${this._clearFilter}
>${this.hass.localize("ui.components.data-table.filtering_by")}
${activeFilters.join(", ")}
<span class="clear">Clear</span></mwc-list-item
>`
: ""}
<ha-check-list-item
left
@request-selected=${this._showDisabledChanged}
@@ -530,11 +523,6 @@ export class HaConfigDeviceDashboard extends LitElement {
ha-button-menu {
margin-left: 8px;
}
.clear {
color: var(--primary-color);
padding-left: 8px;
text-transform: uppercase;
}
`,
haStyle,
];

View File

@@ -1,5 +1,7 @@
import "../../../components/ha-alert";
import "@material/mwc-button/mwc-button";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select/mwc-select";
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
@@ -13,14 +15,12 @@ import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeDomain } from "../../../common/entity/compute_domain";
import { domainIcon } from "../../../common/entity/domain_icon";
import "../../../components/ha-alert";
import "../../../components/ha-area-picker";
import "../../../components/ha-expansion-panel";
import "../../../components/ha-icon-picker";
import "../../../components/ha-select";
import "../../../components/ha-switch";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/ha-textfield";
import type { HaSwitch } from "../../../components/ha-switch";
import {
DeviceRegistryEntry,
subscribeDeviceRegistry,
@@ -161,7 +161,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
></ha-icon-picker>
${OVERRIDE_DEVICE_CLASSES[domain]?.includes(this._deviceClass) ||
(domain === "cover" && this.entry.original_device_class === null)
? html`<ha-select
? html`<mwc-select
.label=${this.hass.localize(
"ui.dialogs.entity_registry.editor.device_class"
)}
@@ -177,7 +177,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
</mwc-list-item>
`
)}
</ha-select>`
</mwc-select>`
: ""}
<ha-textfield
error-message="Domain needs to stay the same"
@@ -420,7 +420,7 @@ export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
padding-bottom: max(env(safe-area-inset-bottom), 8px);
background-color: var(--mdc-theme-surface, #fff);
}
ha-select {
mwc-select {
width: 100%;
}
ha-switch {

View File

@@ -585,15 +585,6 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
)}
.path=${mdiFilterVariant}
></ha-icon-button>
${this.narrow && activeFilters?.length
? html`<mwc-list-item @click=${this._clearFilter}
>${this.hass.localize(
"ui.components.data-table.filtering_by"
)}
${activeFilters.join(", ")}
<span class="clear">Clear</span></mwc-list-item
>`
: ""}
<ha-check-list-item
@request-selected=${this._showDisabledChanged}
.selected=${this._showDisabled}
@@ -905,11 +896,6 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
ha-button-menu {
margin-left: 8px;
}
.clear {
color: var(--primary-color);
padding-left: 8px;
text-transform: uppercase;
}
`,
];
}

View File

@@ -15,7 +15,7 @@ import { ifDefined } from "lit/directives/if-defined";
import memoizeOne from "memoize-one";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import "../../../components/search-input";
import "../../../common/search/search-input";
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
import type { LocalizeFunc } from "../../../common/translations/localize";
import { extractSearchParam } from "../../../common/url/search-params";

View File

@@ -213,11 +213,11 @@ export class HaIntegrationCard extends LitElement {
} else {
stateTextExtra = html`
<br />
<a href=${`/config/logs/?filter=${item.domain}`}>
${this.hass.localize(
<a href="/config/logs"
>${this.hass.localize(
"ui.panel.config.integrations.config_entry.check_the_logs"
)}
</a>
)}</a
>
`;
}
}

View File

@@ -1,8 +1,8 @@
import { HaSelect } from "../../../../../components/ha-select";
import { Select } from "@material/mwc-select";
import { Cluster, ZHADevice } from "../../../../../data/zha";
export interface ItemSelectedEvent {
target?: HaSelect;
target?: Select;
}
export interface ZHADeviceRemovedEvent {

View File

@@ -1,5 +1,6 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select";
import { mdiHelpCircle } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import {
@@ -15,7 +16,6 @@ import { stopPropagation } from "../../../../../common/dom/stop_propagation";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-select";
import "../../../../../components/ha-service-description";
import {
Attribute,
@@ -92,7 +92,7 @@ export class ZHAClusterAttributes extends LitElement {
<ha-card class="content">
<div class="attribute-picker">
<ha-select
<mwc-select
.label=${this.hass!.localize(
"ui.panel.config.zha.cluster_attributes.attributes_of_cluster"
)}
@@ -110,7 +110,7 @@ export class ZHAClusterAttributes extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
</div>
${this.showHelp
? html`
@@ -270,7 +270,7 @@ export class ZHAClusterAttributes extends LitElement {
return [
haStyle,
css`
ha-select {
mwc-select {
margin-top: 16px;
}

View File

@@ -1,4 +1,5 @@
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select";
import { mdiHelpCircle } from "@mdi/js";
import "@polymer/paper-input/paper-input";
import {
@@ -14,7 +15,6 @@ import { stopPropagation } from "../../../../../common/dom/stop_propagation";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-select";
import "../../../../../components/ha-service-description";
import {
Cluster,
@@ -82,7 +82,7 @@ export class ZHAClusterCommands extends LitElement {
<ha-card class="content">
<div class="command-picker">
<ha-select
<mwc-select
.label=${this.hass!.localize(
"ui.panel.config.zha.cluster_commands.commands_of_cluster"
)}
@@ -100,7 +100,7 @@ export class ZHAClusterCommands extends LitElement {
</mwc-list-item>
`
)}
</ha-select>
</mwc-select>
</div>
${this._showHelp
? html`
@@ -206,7 +206,7 @@ export class ZHAClusterCommands extends LitElement {
return [
haStyle,
css`
ha-select {
mwc-select {
margin-top: 16px;
}
.menu {

View File

@@ -1,4 +1,5 @@
import "@material/mwc-list/mwc-list-item";
import "@material/mwc-select";
import { mdiHelpCircle } from "@mdi/js";
import {
css,
@@ -14,7 +15,6 @@ import { stopPropagation } from "../../../../../common/dom/stop_propagation";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-select";
import "../../../../../components/ha-service-description";
import {
Cluster,
@@ -78,7 +78,7 @@ export class ZHAClusters extends LitElement {
<ha-card class="content">
<div class="node-picker">
<ha-select
<mwc-select
.label=${this.hass!.localize(
"ui.panel.config.zha.common.clusters"
)}
@@ -96,7 +96,7 @@ export class ZHAClusters extends LitElement {
>
`
)}
</ha-select>
</mwc-select>
</div>
${this.showHelp
? html`
@@ -137,7 +137,7 @@ export class ZHAClusters extends LitElement {
return [
haStyle,
css`
ha-select {
mwc-select {
margin-top: 16px;
}
.menu {

Some files were not shown because too many files have changed in this diff Show More