mirror of
https://github.com/home-assistant/frontend.git
synced 2026-05-15 21:57:22 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e1474d717 | |||
| 6172c9db23 | |||
| ad36772c74 | |||
| f732de574b |
@@ -1,3 +1,4 @@
|
||||
import "@material/mwc-drawer";
|
||||
import "@material/mwc-top-app-bar-fixed";
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
+10
-25
@@ -1,3 +1,4 @@
|
||||
import "@material/mwc-drawer";
|
||||
import "@material/mwc-top-app-bar-fixed";
|
||||
import { mdiMenu, mdiSwapHorizontal } from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
@@ -6,8 +7,6 @@ import { customElement, query, state } from "lit/decorators";
|
||||
import { dynamicElement } from "../../src/common/dom/dynamic-element-directive";
|
||||
import { setDirectionStyles } from "../../src/common/util/compute_rtl";
|
||||
import "../../src/components/ha-button";
|
||||
import "../../src/components/ha-drawer";
|
||||
import type { HaDrawer } from "../../src/components/ha-drawer";
|
||||
import { HaExpansionPanel } from "../../src/components/ha-expansion-panel";
|
||||
import "../../src/components/ha-icon-button";
|
||||
import "../../src/components/ha-svg-icon";
|
||||
@@ -40,8 +39,8 @@ class HaGallery extends LitElement {
|
||||
@query("notification-manager")
|
||||
private _notifications!: HTMLElementTagNameMap["notification-manager"];
|
||||
|
||||
@query("ha-drawer")
|
||||
private _drawer!: HaDrawer;
|
||||
@query("mwc-drawer")
|
||||
private _drawer!: HTMLElementTagNameMap["mwc-drawer"];
|
||||
|
||||
private _narrow = window.matchMedia("(max-width: 600px)").matches;
|
||||
|
||||
@@ -76,14 +75,15 @@ class HaGallery extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-drawer
|
||||
.direction=${this._rtl ? "rtl" : "ltr"}
|
||||
<mwc-drawer
|
||||
hasHeader
|
||||
.open=${!this._narrow}
|
||||
.type=${this._narrow ? "modal" : "dismissible"}
|
||||
>
|
||||
<div class="drawer-title">Home Assistant Design</div>
|
||||
<span slot="title">Home Assistant Design</span>
|
||||
<!-- <span slot="subtitle">subtitle</span> -->
|
||||
<div class="sidebar">${sidebar}</div>
|
||||
<div slot="appContent" class="app-content">
|
||||
<div slot="appContent">
|
||||
<mwc-top-app-bar-fixed>
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
@@ -144,7 +144,7 @@ class HaGallery extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ha-drawer>
|
||||
</mwc-drawer>
|
||||
<notification-manager
|
||||
.hass=${FAKE_HASS}
|
||||
id="notifications"
|
||||
@@ -226,27 +226,12 @@ class HaGallery extends LitElement {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
--ha-sidebar-width: 256px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
box-sizing: border-box;
|
||||
max-height: calc(100vh - 64px);
|
||||
overflow-y: auto;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.drawer-title {
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
color: var(--primary-text-color);
|
||||
display: flex;
|
||||
font-size: var(--ha-font-size-l);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
min-height: 64px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: var(--primary-text-color);
|
||||
display: block;
|
||||
@@ -270,7 +255,7 @@ class HaGallery extends LitElement {
|
||||
opacity: 0.12;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
div[slot="appContent"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
|
||||
+17
-16
@@ -40,15 +40,15 @@
|
||||
"@codemirror/view": "6.42.1",
|
||||
"@date-fns/tz": "1.4.1",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "7.4.3",
|
||||
"@formatjs/intl-displaynames": "7.3.6",
|
||||
"@formatjs/intl-durationformat": "0.10.9",
|
||||
"@formatjs/intl-getcanonicallocales": "3.2.7",
|
||||
"@formatjs/intl-listformat": "8.3.6",
|
||||
"@formatjs/intl-locale": "5.3.6",
|
||||
"@formatjs/intl-numberformat": "9.3.6",
|
||||
"@formatjs/intl-pluralrules": "6.3.6",
|
||||
"@formatjs/intl-relativetimeformat": "12.3.6",
|
||||
"@formatjs/intl-datetimeformat": "7.4.2",
|
||||
"@formatjs/intl-displaynames": "7.3.5",
|
||||
"@formatjs/intl-durationformat": "0.10.8",
|
||||
"@formatjs/intl-getcanonicallocales": "3.2.6",
|
||||
"@formatjs/intl-listformat": "8.3.5",
|
||||
"@formatjs/intl-locale": "5.3.5",
|
||||
"@formatjs/intl-numberformat": "9.3.5",
|
||||
"@formatjs/intl-pluralrules": "6.3.5",
|
||||
"@formatjs/intl-relativetimeformat": "12.3.5",
|
||||
"@fullcalendar/core": "6.1.20",
|
||||
"@fullcalendar/daygrid": "6.1.20",
|
||||
"@fullcalendar/interaction": "6.1.20",
|
||||
@@ -64,6 +64,7 @@
|
||||
"@lit/reactive-element": "2.1.2",
|
||||
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/mwc-base": "0.27.0",
|
||||
"@material/mwc-drawer": "0.27.0",
|
||||
"@material/mwc-formfield": "patch:@material/mwc-formfield@npm%3A0.27.0#~/.yarn/patches/@material-mwc-formfield-npm-0.27.0-9528cb60f6.patch",
|
||||
"@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"@material/mwc-top-app-bar": "0.27.0",
|
||||
@@ -99,7 +100,7 @@
|
||||
"hls.js": "1.6.16",
|
||||
"home-assistant-js-websocket": "9.6.0",
|
||||
"idb-keyval": "6.2.2",
|
||||
"intl-messageformat": "11.2.5",
|
||||
"intl-messageformat": "11.2.4",
|
||||
"js-yaml": "4.1.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",
|
||||
@@ -141,8 +142,8 @@
|
||||
"@octokit/auth-oauth-device": "8.0.3",
|
||||
"@octokit/plugin-retry": "8.1.0",
|
||||
"@octokit/rest": "22.0.1",
|
||||
"@rsdoctor/rspack-plugin": "1.5.11",
|
||||
"@rspack/core": "2.0.3",
|
||||
"@rsdoctor/rspack-plugin": "1.5.10",
|
||||
"@rspack/core": "2.0.2",
|
||||
"@rspack/dev-server": "2.0.1",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
"@types/chromecast-caf-receiver": "6.0.26",
|
||||
@@ -161,7 +162,7 @@
|
||||
"@types/sortablejs": "1.15.9",
|
||||
"@types/tar": "7.0.87",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@vitest/coverage-v8": "4.1.6",
|
||||
"@vitest/coverage-v8": "4.1.5",
|
||||
"babel-loader": "10.1.1",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"browserslist-useragent-regexp": "4.1.4",
|
||||
@@ -170,7 +171,7 @@
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-import-resolver-webpack": "0.13.11",
|
||||
"eslint-plugin-import-x": "4.16.2",
|
||||
"eslint-plugin-lit": "2.3.1",
|
||||
"eslint-plugin-lit": "2.2.1",
|
||||
"eslint-plugin-lit-a11y": "5.1.1",
|
||||
"eslint-plugin-unused-imports": "4.4.1",
|
||||
"eslint-plugin-wc": "3.1.0",
|
||||
@@ -202,9 +203,9 @@
|
||||
"terser-webpack-plugin": "5.6.0",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "6.0.3",
|
||||
"typescript-eslint": "8.59.3",
|
||||
"typescript-eslint": "8.59.2",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.1.6",
|
||||
"vitest": "4.1.5",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
"webpackbar": "7.0.0",
|
||||
"workbox-build": "patch:workbox-build@npm%3A7.4.1#~/.yarn/patches/workbox-build-npm-7.4.1-c84561662c.patch"
|
||||
|
||||
@@ -54,8 +54,6 @@ export class HaAuthFlow extends LitElement {
|
||||
|
||||
@query("ha-auth-form") private _form?: HaAuthForm;
|
||||
|
||||
@query("ha-form") private _haForm?: HTMLElement;
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
@@ -162,8 +160,9 @@ export class HaAuthFlow extends LitElement {
|
||||
|
||||
// 100ms to give all the form elements time to initialize.
|
||||
setTimeout(() => {
|
||||
if (this._haForm) {
|
||||
(this._haForm as any).focus();
|
||||
const form = this.renderRoot.querySelector("ha-form");
|
||||
if (form) {
|
||||
(form as any).focus();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { isComponentLoaded } from "./is_component_loaded";
|
||||
|
||||
export const canShowPage = (hass: HomeAssistant, page: PageNavigation) =>
|
||||
(isCore(page) || isLoadedIntegration(hass, page)) &&
|
||||
!hideAdvancedPage(hass, page) &&
|
||||
isNotLoadedIntegration(hass, page);
|
||||
|
||||
export const isLoadedIntegration = (
|
||||
@@ -26,3 +27,8 @@ export const isNotLoadedIntegration = (
|
||||
);
|
||||
|
||||
export const isCore = (page: PageNavigation) => page.core;
|
||||
export const isAdvancedPage = (page: PageNavigation) => page.advancedOnly;
|
||||
export const userWantsAdvanced = (hass: HomeAssistant) =>
|
||||
hass.userData?.showAdvanced;
|
||||
export const hideAdvancedPage = (hass: HomeAssistant, page: PageNavigation) =>
|
||||
isAdvancedPage(page) && !userWantsAdvanced(hass);
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import type { TooltipPositionCallback } from "echarts/types/dist/shared";
|
||||
|
||||
export const TOOLTIP_GAP_PX = 12;
|
||||
export const TOOLTIP_TOP_OFFSET_PX = 10;
|
||||
|
||||
/**
|
||||
* Pins the tooltip near the top of the chart and offsets it horizontally
|
||||
* from the cursor so it never covers the data point being inspected.
|
||||
* For axis-trigger time-series tooltips where the cursor's Y is uncorrelated
|
||||
* with the displayed content.
|
||||
*/
|
||||
export const sideTooltipPosition: TooltipPositionCallback = (
|
||||
point,
|
||||
_params,
|
||||
dom,
|
||||
_rect,
|
||||
size
|
||||
) => {
|
||||
const [cursorX] = point;
|
||||
const [viewW, viewH] = size.viewSize;
|
||||
const [tipW, tipH] = size.contentSize;
|
||||
|
||||
const rtl =
|
||||
dom instanceof HTMLElement && getComputedStyle(dom).direction === "rtl";
|
||||
|
||||
const rightOfCursor = cursorX + TOOLTIP_GAP_PX;
|
||||
const leftOfCursor = cursorX - TOOLTIP_GAP_PX - tipW;
|
||||
|
||||
let x = rtl ? leftOfCursor : rightOfCursor;
|
||||
const overflowsRight = x + tipW > viewW;
|
||||
const overflowsLeft = x < 0;
|
||||
if (overflowsRight || overflowsLeft) {
|
||||
x = rtl ? rightOfCursor : leftOfCursor;
|
||||
}
|
||||
x = Math.max(0, Math.min(x, viewW - tipW));
|
||||
|
||||
const y = Math.max(0, Math.min(TOOLTIP_TOP_OFFSET_PX, viewH - tipH));
|
||||
|
||||
return [x, y];
|
||||
};
|
||||
@@ -19,7 +19,7 @@ import type {
|
||||
} from "echarts/types/dist/shared";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
@@ -102,8 +102,6 @@ export class HaChartBase extends LitElement {
|
||||
|
||||
@state() private _hiddenDatasets = new Set<string>();
|
||||
|
||||
@query(".chart") private _chartContainer?: HTMLDivElement;
|
||||
|
||||
private _modifierPressed = false;
|
||||
|
||||
private _isTouchDevice = "ontouchstart" in window;
|
||||
@@ -471,6 +469,7 @@ export class HaChartBase extends LitElement {
|
||||
|
||||
private async _setupChart() {
|
||||
if (this._loading) return;
|
||||
const container = this.renderRoot.querySelector(".chart") as HTMLDivElement;
|
||||
this._loading = true;
|
||||
try {
|
||||
if (this.chart) {
|
||||
@@ -485,7 +484,7 @@ export class HaChartBase extends LitElement {
|
||||
const style = getComputedStyle(this);
|
||||
echarts.registerTheme("custom", this._createTheme(style));
|
||||
|
||||
this.chart = echarts.init(this._chartContainer!, "custom");
|
||||
this.chart = echarts.init(container, "custom");
|
||||
this.chart.on("datazoom", (e: any) => {
|
||||
this._handleDataZoomEvent(e);
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import type { LineChartEntity, LineChartState } from "../../data/history";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
||||
import { sideTooltipPosition } from "./chart-tooltip-position";
|
||||
import type { ECOption } from "../../resources/echarts/echarts";
|
||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||
import {
|
||||
@@ -414,7 +413,8 @@ export class StateHistoryChartLine extends LitElement {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
renderMode: "html",
|
||||
position: sideTooltipPosition,
|
||||
position: "bottom",
|
||||
align: "center",
|
||||
confine: true,
|
||||
formatter: this._renderTooltip,
|
||||
},
|
||||
|
||||
@@ -14,7 +14,6 @@ import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import type { TimelineEntity } from "../../data/history";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
||||
import { sideTooltipPosition } from "./chart-tooltip-position";
|
||||
import { computeTimelineColor } from "./timeline-color";
|
||||
import type { ECOption } from "../../resources/echarts/echarts";
|
||||
import echarts from "../../resources/echarts/echarts";
|
||||
@@ -264,7 +263,8 @@ export class StateHistoryChartTimeline extends LitElement {
|
||||
},
|
||||
tooltip: {
|
||||
renderMode: "html",
|
||||
position: sideTooltipPosition,
|
||||
position: "bottom",
|
||||
align: "center",
|
||||
confine: true,
|
||||
formatter: this._renderTooltip,
|
||||
},
|
||||
|
||||
@@ -2,13 +2,7 @@ import type { RenderItemFunction } from "@lit-labs/virtualizer/virtualize";
|
||||
import { mdiRestart } from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import {
|
||||
customElement,
|
||||
eventOptions,
|
||||
property,
|
||||
queryAll,
|
||||
state,
|
||||
} from "lit/decorators";
|
||||
import { customElement, eventOptions, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||
import type {
|
||||
@@ -110,11 +104,6 @@ export class StateHistoryCharts extends LitElement {
|
||||
|
||||
@state() private _hasZoomedCharts = false;
|
||||
|
||||
@queryAll("state-history-chart-line, state-history-chart-timeline")
|
||||
private _chartComponents!: NodeListOf<
|
||||
StateHistoryChartLine | StateHistoryChartTimeline
|
||||
>;
|
||||
|
||||
private _isSyncing = false;
|
||||
|
||||
// @ts-ignore
|
||||
@@ -338,7 +327,11 @@ export class StateHistoryCharts extends LitElement {
|
||||
this._isSyncing = true;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
this._chartComponents.forEach((chartComponent, index) => {
|
||||
const chartComponents = this.renderRoot.querySelectorAll(
|
||||
"state-history-chart-line, state-history-chart-timeline"
|
||||
) as unknown as (StateHistoryChartLine | StateHistoryChartTimeline)[];
|
||||
|
||||
chartComponents.forEach((chartComponent, index) => {
|
||||
if (index === sourceChartIndex) {
|
||||
return;
|
||||
}
|
||||
@@ -357,7 +350,11 @@ export class StateHistoryCharts extends LitElement {
|
||||
this._isSyncing = true;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
this._chartComponents.forEach((chartComponent: any) => {
|
||||
const chartComponents = this.renderRoot.querySelectorAll(
|
||||
"state-history-chart-line, state-history-chart-timeline"
|
||||
);
|
||||
|
||||
chartComponents.forEach((chartComponent: any) => {
|
||||
const chartBase =
|
||||
chartComponent.renderRoot?.querySelector("ha-chart-base");
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ import type { HomeAssistant } from "../../types";
|
||||
import { getPeriodicAxisLabelConfig } from "./axis-label";
|
||||
import type { CustomLegendOption } from "./ha-chart-base";
|
||||
import "./ha-chart-base";
|
||||
import { sideTooltipPosition } from "./chart-tooltip-position";
|
||||
import { fillDataGapsAndRoundCaps } from "./round-caps";
|
||||
|
||||
export const supportedStatTypeMap: Record<StatisticType, StatisticType> = {
|
||||
@@ -143,8 +142,7 @@ export class StatisticsChart extends LitElement {
|
||||
changedProps.has("statTypes") ||
|
||||
changedProps.has("chartType") ||
|
||||
changedProps.has("hideLegend") ||
|
||||
changedProps.has("_hiddenStats") ||
|
||||
changedProps.has("names")
|
||||
changedProps.has("_hiddenStats")
|
||||
) {
|
||||
this._generateData();
|
||||
}
|
||||
@@ -461,7 +459,8 @@ export class StatisticsChart extends LitElement {
|
||||
tooltip: {
|
||||
trigger: "axis",
|
||||
renderMode: "html",
|
||||
position: sideTooltipPosition,
|
||||
position: "bottom",
|
||||
align: "center",
|
||||
confine: true,
|
||||
formatter: this._renderTooltip,
|
||||
},
|
||||
|
||||
@@ -24,7 +24,6 @@ import "../ha-icon-button";
|
||||
import "../ha-icon-button-next";
|
||||
import "../ha-icon-button-prev";
|
||||
import "../ha-textarea";
|
||||
import type { HaTextArea } from "../ha-textarea";
|
||||
import "./date-range-picker";
|
||||
|
||||
export type DateRangePickerRanges = Record<string, [Date, Date]>;
|
||||
@@ -99,8 +98,6 @@ export class HaDateRangePicker extends LitElement {
|
||||
|
||||
@query(".container") private _containerElement?: HTMLDivElement;
|
||||
|
||||
@query("ha-textarea") private _textareaElement?: HaTextArea;
|
||||
|
||||
private _narrow = false;
|
||||
|
||||
private _unsubscribeTinyKeys?: () => void;
|
||||
@@ -338,8 +335,9 @@ export class HaDateRangePicker extends LitElement {
|
||||
};
|
||||
|
||||
private _setTextareaFocusStyle(focused: boolean) {
|
||||
if (this._textareaElement) {
|
||||
this._textareaElement.setFocused(focused);
|
||||
const textarea = this.renderRoot.querySelector("ha-textarea");
|
||||
if (textarea) {
|
||||
textarea.setFocused(focused);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,6 @@ export class HaStatisticPicker extends LitElement {
|
||||
private async _getStatisticIds() {
|
||||
this.statisticIds = await getStatisticIds(this.hass, this.statisticTypes);
|
||||
this._picker?.requestUpdate();
|
||||
this._valueRenderer = this._makeValueRenderer();
|
||||
}
|
||||
|
||||
private _getItems = () =>
|
||||
@@ -318,7 +317,7 @@ export class HaStatisticPicker extends LitElement {
|
||||
}
|
||||
);
|
||||
|
||||
private _renderValue(value: string) {
|
||||
private _valueRenderer: PickerValueRenderer = (value) => {
|
||||
const statisticId = value;
|
||||
|
||||
const item = this._computeItem(statisticId);
|
||||
@@ -342,13 +341,7 @@ export class HaStatisticPicker extends LitElement {
|
||||
? html`<span slot="supporting-text">${item.secondary}</span>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
private _makeValueRenderer(): PickerValueRenderer {
|
||||
return (value) => this._renderValue(value);
|
||||
}
|
||||
|
||||
private _valueRenderer: PickerValueRenderer = this._makeValueRenderer();
|
||||
};
|
||||
|
||||
private _computeItem(statisticId: string): StatisticComboBoxItem {
|
||||
const stateObj = this.hass.states[statisticId];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "@home-assistant/webawesome/dist/components/popover/popover";
|
||||
import { css, html, nothing, type PropertyValues } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { ScrollLockMixin } from "../mixins/scroll-lock-mixin";
|
||||
@@ -25,8 +25,6 @@ export class HaAdaptivePopover extends ScrollLockMixin(HaAdaptiveDialog) {
|
||||
|
||||
@state() private _shouldRenderPopover = false;
|
||||
|
||||
@query("wa-popover") private _popoverElement?: HTMLElement;
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>) {
|
||||
if (
|
||||
changedProperties.has("dialogAnchor") ||
|
||||
@@ -190,7 +188,7 @@ export class HaAdaptivePopover extends ScrollLockMixin(HaAdaptiveDialog) {
|
||||
}
|
||||
|
||||
private _handlePopoverPointerDown(ev: PointerEvent) {
|
||||
const popover = this._popoverElement;
|
||||
const popover = this.renderRoot.querySelector("wa-popover");
|
||||
const dialog = popover?.shadowRoot?.querySelector(
|
||||
"dialog"
|
||||
) as HTMLDialogElement | null;
|
||||
@@ -217,7 +215,7 @@ export class HaAdaptivePopover extends ScrollLockMixin(HaAdaptiveDialog) {
|
||||
}
|
||||
|
||||
private _pulsePopover() {
|
||||
const popover = this._popoverElement;
|
||||
const popover = this.renderRoot.querySelector("wa-popover");
|
||||
const popup = popover?.shadowRoot?.querySelector("wa-popup") as {
|
||||
popup?: HTMLElement;
|
||||
} | null;
|
||||
|
||||
@@ -5,10 +5,10 @@ import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import type { Analytics, AnalyticsPreferences } from "../data/analytics";
|
||||
import { haStyle } from "../resources/styles";
|
||||
import "./ha-md-list-item";
|
||||
import "./ha-switch";
|
||||
import type { HaSwitch } from "./ha-switch";
|
||||
import "./ha-tooltip";
|
||||
import "./item/ha-row-item";
|
||||
import type { HaSwitch } from "./ha-switch";
|
||||
|
||||
const ADDITIONAL_PREFERENCES = ["usage", "statistics"] as const;
|
||||
|
||||
@@ -33,7 +33,7 @@ export class HaAnalytics extends LitElement {
|
||||
const baseEnabled = !loading && this.analytics!.preferences.base;
|
||||
|
||||
return html`
|
||||
<ha-row-item>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline"
|
||||
>${this.localize(
|
||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.base.title`
|
||||
@@ -52,10 +52,10 @@ export class HaAnalytics extends LitElement {
|
||||
.disabled=${loading}
|
||||
name="base"
|
||||
></ha-switch>
|
||||
</ha-row-item>
|
||||
</ha-md-list-item>
|
||||
${ADDITIONAL_PREFERENCES.map(
|
||||
(preference) => html`
|
||||
<ha-row-item>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline"
|
||||
>${this.localize(
|
||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.${preference}.title`
|
||||
@@ -81,10 +81,10 @@ export class HaAnalytics extends LitElement {
|
||||
`ui.panel.${this.translationKeyPanel}.analytics.need_base_enabled`
|
||||
)}
|
||||
</ha-tooltip>`}
|
||||
</ha-row-item>
|
||||
</ha-md-list-item>
|
||||
`
|
||||
)}
|
||||
<ha-row-item>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline"
|
||||
>${this.localize(
|
||||
`ui.panel.${this.translationKeyPanel}.analytics.preferences.diagnostics.title`
|
||||
@@ -103,7 +103,7 @@ export class HaAnalytics extends LitElement {
|
||||
.disabled=${loading}
|
||||
name="diagnostics"
|
||||
></ha-switch>
|
||||
</ha-row-item>
|
||||
</ha-md-list-item>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -139,8 +139,10 @@ export class HaAnalytics extends LitElement {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
ha-row-item {
|
||||
--ha-row-item-padding-inline: 0;
|
||||
ha-md-list-item {
|
||||
--md-list-item-leading-space: 0;
|
||||
--md-list-item-trailing-space: 0;
|
||||
--md-item-overflow: visible;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
customElement,
|
||||
property,
|
||||
query,
|
||||
queryAll,
|
||||
state as litState,
|
||||
} from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
@@ -32,8 +31,6 @@ export class HaAnsiToHtml extends LitElement {
|
||||
|
||||
@query("pre") private _pre?: HTMLPreElement;
|
||||
|
||||
@queryAll("div") private _divs!: NodeListOf<HTMLDivElement>;
|
||||
|
||||
@litState() private _filter = "";
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@@ -323,7 +320,7 @@ export class HaAnsiToHtml extends LitElement {
|
||||
*/
|
||||
filterLines(filter: string): boolean {
|
||||
this._filter = filter;
|
||||
const lines = this._divs;
|
||||
const lines = this.shadowRoot?.querySelectorAll("div") || [];
|
||||
let numberOfFoundLines = 0;
|
||||
if (!filter) {
|
||||
lines.forEach((line) => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
@@ -24,12 +24,11 @@ class HaBluePrintPicker extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@query("ha-select") private _select?: HTMLElement;
|
||||
|
||||
public open() {
|
||||
if (this._select) {
|
||||
const select = this.shadowRoot?.querySelector("ha-select");
|
||||
if (select) {
|
||||
// @ts-expect-error
|
||||
this._select.menuOpen = true;
|
||||
select.menuOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,6 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
@query("#body") private _bodyElement!: HTMLDivElement;
|
||||
|
||||
@query("[autofocus]") private _autofocusElement?: HTMLElement;
|
||||
|
||||
protected get scrollableElement(): HTMLElement | null {
|
||||
return this._bodyElement;
|
||||
}
|
||||
@@ -95,12 +93,12 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
await this.updateComplete;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const element = this._autofocusElement;
|
||||
if (
|
||||
this._hassConfig?.auth.external &&
|
||||
isIosApp(this._hassConfig.auth.external)
|
||||
) {
|
||||
if (element) {
|
||||
const element = this.renderRoot.querySelector("[autofocus]");
|
||||
if (element !== null) {
|
||||
if (!element.id) {
|
||||
element.id = "ha-bottom-sheet-autofocus";
|
||||
}
|
||||
@@ -113,7 +111,9 @@ export class HaBottomSheet extends ScrollableFadeMixin(LitElement) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
element?.focus();
|
||||
(
|
||||
this.renderRoot.querySelector("[autofocus]") as HTMLElement | null
|
||||
)?.focus();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, ReactiveElement, render } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import type { ContextType } from "@lit/context";
|
||||
import { consume } from "@lit/context";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
@@ -44,14 +43,7 @@ import type {
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { showToast } from "../util/toast";
|
||||
import { documentationUrl } from "../util/documentation-url";
|
||||
import {
|
||||
internationalizationContext,
|
||||
registriesContext,
|
||||
statesContext,
|
||||
labelsContext,
|
||||
configContext,
|
||||
formattersContext,
|
||||
} from "../data/context";
|
||||
import { labelsContext } from "../data/context";
|
||||
import type { LabelRegistryEntry } from "../data/label/label_registry";
|
||||
import "./ha-code-editor-completion-items";
|
||||
import type { CompletionItem } from "./ha-code-editor-completion-items";
|
||||
@@ -86,6 +78,8 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
|
||||
@property() public mode = "yaml";
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
|
||||
// eslint-disable-next-line lit/no-native-attributes
|
||||
@property({ type: Boolean }) public autofocus = false;
|
||||
|
||||
@@ -129,29 +123,9 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
|
||||
@state() private _canCopy = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: configContext, subscribe: true })
|
||||
private _config?: ContextType<typeof configContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n?: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: labelsContext, subscribe: true })
|
||||
private _labels?: ContextType<typeof labelsContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: registriesContext, subscribe: true })
|
||||
private _registries?: ContextType<typeof registriesContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: formattersContext, subscribe: true })
|
||||
private _formatters?: ContextType<typeof formattersContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: statesContext, subscribe: true })
|
||||
private _states?: ContextType<typeof statesContext>;
|
||||
private _labels?: LabelRegistryEntry[];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
private _loadedCodeMirror?: typeof import("../resources/codemirror");
|
||||
@@ -188,7 +162,6 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
this.codemirror.state,
|
||||
[this._loadedCodeMirror.tags.comment]
|
||||
);
|
||||
// eslint-disable-next-line lit/prefer-query-decorators
|
||||
return !!this.renderRoot.querySelector(`span.${className}`);
|
||||
}
|
||||
|
||||
@@ -216,9 +189,9 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
const line = doc.lineAt(pos);
|
||||
const message = `${
|
||||
err.reason ||
|
||||
this._i18n?.localize("ui.components.yaml-editor.error") ||
|
||||
this.hass?.localize("ui.components.yaml-editor.error") ||
|
||||
"YAML syntax error"
|
||||
}${err.mark ? ` (${this._i18n?.localize("ui.components.yaml-editor.error_location", { line: err.mark.line + 1, column: err.mark.column + 1 })})` : ""}`;
|
||||
}${err.mark ? ` (${this.hass?.localize("ui.components.yaml-editor.error_location", { line: err.mark.line + 1, column: err.mark.column + 1 })})` : ""}`;
|
||||
diagnostics = [{ from: pos, to: line.to, severity: "error", message }];
|
||||
}
|
||||
this.codemirror.dispatch(
|
||||
@@ -423,8 +396,8 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
this._loadedCodeMirror!.haJinjaHoverSource(
|
||||
view,
|
||||
pos,
|
||||
this._config ? documentationUrl(this._config, "") : undefined,
|
||||
this._hassArgHoverContext()
|
||||
this.hass ? documentationUrl(this.hass, "") : undefined,
|
||||
this.hass ? this._hassArgHoverContext() : undefined
|
||||
),
|
||||
{ hoverTime: 300 }
|
||||
),
|
||||
@@ -435,7 +408,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
const completionSources: CompletionSource[] = [
|
||||
this._loadedCodeMirror.haJinjaCompletionSource,
|
||||
];
|
||||
if (this.autocompleteEntities) {
|
||||
if (this.autocompleteEntities && this.hass) {
|
||||
completionSources.push(this._entityCompletions.bind(this));
|
||||
}
|
||||
if (this.autocompleteIcons) {
|
||||
@@ -474,12 +447,12 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
private _fullscreenLabel(): string {
|
||||
if (this._isFullscreen) {
|
||||
return (
|
||||
this._i18n?.localize("ui.components.yaml-editor.exit_fullscreen") ||
|
||||
this.hass?.localize("ui.components.yaml-editor.exit_fullscreen") ||
|
||||
"Exit fullscreen"
|
||||
);
|
||||
}
|
||||
return (
|
||||
this._i18n?.localize("ui.components.yaml-editor.enter_fullscreen") ||
|
||||
this.hass?.localize("ui.components.yaml-editor.enter_fullscreen") ||
|
||||
"Enter fullscreen"
|
||||
);
|
||||
}
|
||||
@@ -534,7 +507,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
{
|
||||
id: "test",
|
||||
label:
|
||||
this._i18n?.localize(
|
||||
this.hass?.localize(
|
||||
`ui.components.yaml-editor.test_${this.testing ? "off" : "on"}`
|
||||
) || "Test",
|
||||
path: this.testing ? mdiBugOutline : mdiBug,
|
||||
@@ -545,14 +518,14 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
{
|
||||
id: "undo",
|
||||
disabled: !this._canUndo,
|
||||
label: this._i18n?.localize("ui.common.undo") || "Undo",
|
||||
label: this.hass?.localize("ui.common.undo") || "Undo",
|
||||
path: mdiUndo,
|
||||
action: (e: Event) => this._handleUndoClick(e),
|
||||
},
|
||||
{
|
||||
id: "redo",
|
||||
disabled: !this._canRedo,
|
||||
label: this._i18n?.localize("ui.common.redo") || "Redo",
|
||||
label: this.hass?.localize("ui.common.redo") || "Redo",
|
||||
path: mdiRedo,
|
||||
action: (e: Event) => this._handleRedoClick(e),
|
||||
},
|
||||
@@ -560,7 +533,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
id: "copy",
|
||||
disabled: !this._canCopy,
|
||||
label:
|
||||
this._i18n?.localize("ui.components.yaml-editor.copy_to_clipboard") ||
|
||||
this.hass?.localize("ui.components.yaml-editor.copy_to_clipboard") ||
|
||||
"Copy to Clipboard",
|
||||
path: mdiContentCopy,
|
||||
action: (e: Event) => this._handleClipboardClick(e),
|
||||
@@ -568,7 +541,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
{
|
||||
id: "find-replace",
|
||||
label:
|
||||
this._i18n?.localize("ui.components.yaml-editor.find_and_replace") ||
|
||||
this.hass?.localize("ui.components.yaml-editor.find_and_replace") ||
|
||||
"Find and replace",
|
||||
path: mdiFindReplace,
|
||||
action: (e: Event) => this._handleFindReplaceClick(e),
|
||||
@@ -610,7 +583,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
await copyToClipboard(this.value);
|
||||
showToast(this, {
|
||||
message:
|
||||
this._i18n?.localize("ui.common.copied_clipboard") ||
|
||||
this.hass?.localize("ui.common.copied_clipboard") ||
|
||||
"Copied to clipboard",
|
||||
});
|
||||
}
|
||||
@@ -678,11 +651,12 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
};
|
||||
|
||||
/**
|
||||
* Builds a HassArgHoverContext from the context objects so that
|
||||
* Builds a HassArgHoverContext from the current hass object so that
|
||||
* haJinjaHoverSource can resolve entity / device / area friendly names
|
||||
* without importing the full HomeAssistant type into the resource file.
|
||||
*/
|
||||
private _hassArgHoverContext(): HassArgHoverContext {
|
||||
const hass = this.hass!;
|
||||
const labelMap: Record<
|
||||
string,
|
||||
{ name: string; description?: string | null }
|
||||
@@ -694,33 +668,27 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
};
|
||||
}
|
||||
return {
|
||||
states: this._states as HassArgHoverContext["states"],
|
||||
devices: this._registries?.devices as HassArgHoverContext["devices"],
|
||||
areas: this._registries?.areas as HassArgHoverContext["areas"],
|
||||
floors: this._registries?.floors as HassArgHoverContext["floors"],
|
||||
entities: this._registries?.entities as HassArgHoverContext["entities"],
|
||||
states: hass.states as HassArgHoverContext["states"],
|
||||
devices: hass.devices as HassArgHoverContext["devices"],
|
||||
areas: hass.areas as HassArgHoverContext["areas"],
|
||||
floors: hass.floors as HassArgHoverContext["floors"],
|
||||
entities: hass.entities as HassArgHoverContext["entities"],
|
||||
labels: labelMap,
|
||||
formatEntityState: (entityId) =>
|
||||
this._formatters!.formatEntityState(this._states![entityId]),
|
||||
hass.formatEntityState(hass.states[entityId]),
|
||||
formatEntityName: (entityId) => {
|
||||
const stateObj = this._states?.[entityId];
|
||||
const stateObj = hass.states[entityId];
|
||||
return (
|
||||
(stateObj?.attributes.friendly_name as string | undefined) ??
|
||||
this._registries?.entities?.[entityId]?.name ??
|
||||
hass.entities[entityId]?.name ??
|
||||
undefined
|
||||
);
|
||||
},
|
||||
formatAttributeName: (entityId, attribute) =>
|
||||
this._formatters!.formatEntityAttributeName(
|
||||
this._states![entityId],
|
||||
attribute
|
||||
),
|
||||
hass.formatEntityAttributeName(hass.states[entityId], attribute),
|
||||
formatAttributeValue: (entityId, attribute) =>
|
||||
this._formatters!.formatEntityAttributeValue(
|
||||
this._states![entityId],
|
||||
attribute
|
||||
),
|
||||
localize: (key) => this._i18n!.localize(key as never),
|
||||
hass.formatEntityAttributeValue(hass.states[entityId], attribute),
|
||||
localize: (key) => hass.localize(key as never),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -730,51 +698,49 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
? completion.apply
|
||||
: completion.label;
|
||||
const context = getEntityContext(
|
||||
this._states![key],
|
||||
this._registries!.entities,
|
||||
this._registries!.devices,
|
||||
this._registries!.areas,
|
||||
this._registries!.floors
|
||||
this.hass!.states[key],
|
||||
this.hass!.entities,
|
||||
this.hass!.devices,
|
||||
this.hass!.areas,
|
||||
this.hass!.floors
|
||||
);
|
||||
|
||||
const completionInfo = document.createElement("div");
|
||||
completionInfo.classList.add("completion-info");
|
||||
|
||||
const formattedState = this._formatters!.formatEntityState(
|
||||
this._states![key]
|
||||
);
|
||||
const formattedState = this.hass!.formatEntityState(this.hass!.states[key]);
|
||||
|
||||
const completionItems: CompletionItem[] = [
|
||||
{
|
||||
label: this._i18n!.localize(
|
||||
label: this.hass!.localize(
|
||||
"ui.components.entity.entity-state-picker.state"
|
||||
),
|
||||
value: formattedState,
|
||||
subValue:
|
||||
// If the state exactly matches the formatted state, don't show the raw state
|
||||
this._states![key].state === formattedState
|
||||
this.hass!.states[key].state === formattedState
|
||||
? undefined
|
||||
: this._states![key].state,
|
||||
: this.hass!.states[key].state,
|
||||
},
|
||||
];
|
||||
|
||||
if (context.device && context.device.name) {
|
||||
completionItems.push({
|
||||
label: this._i18n!.localize("ui.components.device-picker.device"),
|
||||
label: this.hass!.localize("ui.components.device-picker.device"),
|
||||
value: context.device.name,
|
||||
});
|
||||
}
|
||||
|
||||
if (context.area && context.area.name) {
|
||||
completionItems.push({
|
||||
label: this._i18n!.localize("ui.components.area-picker.area"),
|
||||
label: this.hass!.localize("ui.components.area-picker.area"),
|
||||
value: context.area.name,
|
||||
});
|
||||
}
|
||||
|
||||
if (context.floor && context.floor.name) {
|
||||
completionItems.push({
|
||||
label: this._i18n!.localize("ui.components.floor-picker.floor"),
|
||||
label: this.hass!.localize("ui.components.floor-picker.floor"),
|
||||
value: context.floor.name,
|
||||
});
|
||||
}
|
||||
@@ -795,15 +761,15 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
entityId: string,
|
||||
attribute: string
|
||||
): CompletionInfo | null => {
|
||||
if (!this._states || !this._formatters) return null;
|
||||
const stateObj = this._states[entityId];
|
||||
if (!this.hass) return null;
|
||||
const stateObj = this.hass.states[entityId];
|
||||
if (!stateObj) return null;
|
||||
|
||||
const translatedName = this._formatters.formatEntityAttributeName(
|
||||
const translatedName = this.hass.formatEntityAttributeName(
|
||||
stateObj,
|
||||
attribute
|
||||
);
|
||||
const formattedValue = this._formatters.formatEntityAttributeValue(
|
||||
const formattedValue = this.hass.formatEntityAttributeValue(
|
||||
stateObj,
|
||||
attribute
|
||||
);
|
||||
@@ -843,9 +809,9 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
completion: Completion
|
||||
): CompletionInfo | Promise<CompletionInfo> | null => {
|
||||
if (
|
||||
this._states &&
|
||||
this.hass &&
|
||||
typeof completion.apply === "string" &&
|
||||
completion.apply in this._states
|
||||
completion.apply in this.hass.states
|
||||
) {
|
||||
return this._renderInfo(completion);
|
||||
}
|
||||
@@ -1054,7 +1020,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
private _statesDotNotationCompletions(
|
||||
context: CompletionContext
|
||||
): CompletionResult | null | undefined {
|
||||
if (!this._states) return undefined;
|
||||
if (!this.hass) return undefined;
|
||||
|
||||
const { state: editorState, pos } = context;
|
||||
const tree = this._loadedCodeMirror!.syntaxTree(editorState);
|
||||
@@ -1163,7 +1129,9 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
case 0: {
|
||||
// states. → offer all unique domains
|
||||
const domains = [
|
||||
...new Set(Object.keys(this._states).map((id) => id.split(".")[0])),
|
||||
...new Set(
|
||||
Object.keys(this.hass.states).map((id) => id.split(".")[0])
|
||||
),
|
||||
].sort();
|
||||
return {
|
||||
from: completionFrom,
|
||||
@@ -1174,7 +1142,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
case 1: {
|
||||
// states.<domain>. → offer entity object_ids for that domain
|
||||
const [domain] = segments;
|
||||
const entities = Object.keys(this._states)
|
||||
const entities = Object.keys(this.hass.states)
|
||||
.filter((id) => id.startsWith(`${domain}.`))
|
||||
.map((id) => id.split(".").slice(1).join("."));
|
||||
if (!entities.length) return { from: completionFrom, options: [] };
|
||||
@@ -1204,7 +1172,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
}
|
||||
// Offer attribute names from the entity's state object
|
||||
const entityId = `${domain}.${entity}`;
|
||||
const entityState = this._states[entityId];
|
||||
const entityState = this.hass.states[entityId];
|
||||
if (!entityState) return { from: completionFrom, options: [] };
|
||||
const attrNames = Object.keys(entityState.attributes).sort();
|
||||
return {
|
||||
@@ -1374,8 +1342,8 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
): CompletionResult {
|
||||
const from = stringNode.from + 1;
|
||||
const empty: CompletionResult = { from, options: [] };
|
||||
if (!entityId || !this._states) return empty;
|
||||
const entityState = this._states[entityId];
|
||||
if (!entityId || !this.hass) return empty;
|
||||
const entityState = this.hass.states[entityId];
|
||||
if (!entityState) return empty;
|
||||
const attrs = Object.keys(entityState.attributes).sort();
|
||||
if (!attrs.length) return empty;
|
||||
@@ -1395,7 +1363,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
from: number;
|
||||
to: number;
|
||||
}): CompletionResult | null {
|
||||
const states = this._getStates(this._states!);
|
||||
const states = this._getStates(this.hass!.states);
|
||||
if (!states?.length) return null;
|
||||
// from is stringNode.from + 1 to skip the opening quote character.
|
||||
const from = stringNode.from + 1;
|
||||
@@ -1429,8 +1397,8 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
from: number;
|
||||
to: number;
|
||||
}): CompletionResult | null {
|
||||
if (!this._registries?.devices) return null;
|
||||
const devices = this._getDevices(this._registries.devices);
|
||||
if (!this.hass?.devices) return null;
|
||||
const devices = this._getDevices(this.hass.devices);
|
||||
if (!devices.length) return null;
|
||||
return {
|
||||
from: stringNode.from + 1,
|
||||
@@ -1458,8 +1426,8 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
from: number;
|
||||
to: number;
|
||||
}): CompletionResult | null {
|
||||
if (!this._registries?.areas) return null;
|
||||
const areas = this._getAreas(this._registries.areas);
|
||||
if (!this.hass?.areas) return null;
|
||||
const areas = this._getAreas(this.hass.areas);
|
||||
if (!areas.length) return null;
|
||||
return {
|
||||
from: stringNode.from + 1,
|
||||
@@ -1487,8 +1455,8 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
from: number;
|
||||
to: number;
|
||||
}): CompletionResult | null {
|
||||
if (!this._registries?.floors) return null;
|
||||
const floors = this._getFloors(this._registries.floors);
|
||||
if (!this.hass?.floors) return null;
|
||||
const floors = this._getFloors(this.hass.floors);
|
||||
if (!floors.length) return null;
|
||||
return {
|
||||
from: stringNode.from + 1,
|
||||
@@ -1588,7 +1556,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
|
||||
// If cursor is after the entity field, show all entities
|
||||
if (context.pos >= afterField) {
|
||||
const states = this._getStates(this._states!);
|
||||
const states = this._getStates(this.hass!.states);
|
||||
|
||||
if (!states || !states.length) {
|
||||
return null;
|
||||
@@ -1643,7 +1611,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
const afterListMarker = currentLine.from + listItemMatch[0].length;
|
||||
|
||||
if (context.pos >= afterListMarker) {
|
||||
const states = this._getStates(this._states!);
|
||||
const states = this._getStates(this.hass!.states);
|
||||
|
||||
if (!states || !states.length) {
|
||||
return null;
|
||||
@@ -1703,7 +1671,7 @@ export class HaCodeEditor extends ReactiveElement {
|
||||
return null;
|
||||
}
|
||||
|
||||
const states = this._getStates(this._states!);
|
||||
const states = this._getStates(this.hass!.states);
|
||||
|
||||
if (!states || !states.length) {
|
||||
return null;
|
||||
|
||||
@@ -54,7 +54,6 @@ export class HaControlSelect extends LitElement {
|
||||
this._activeIndex = index;
|
||||
this.requestUpdate();
|
||||
this.updateComplete.then(() => {
|
||||
// eslint-disable-next-line lit/prefer-query-decorators
|
||||
const option = this.shadowRoot?.querySelector(
|
||||
`#option-${this.options![index].value}`
|
||||
) as HTMLElement;
|
||||
|
||||
+125
-265
@@ -1,115 +1,36 @@
|
||||
import "@home-assistant/webawesome/dist/components/drawer/drawer";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { DrawerBase } from "@material/mwc-drawer/mwc-drawer-base";
|
||||
import { styles } from "@material/mwc-drawer/mwc-drawer.css";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { HASSDomEvent } from "../common/dom/fire_event";
|
||||
import { SwipeGestureRecognizer } from "../common/util/swipe-gesture-recognizer";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"hass-drawer-closed": undefined;
|
||||
"hass-layout-transition": { active: boolean; reason?: string };
|
||||
}
|
||||
interface HTMLElementEventMap {
|
||||
"hass-drawer-closed": HASSDomEvent<HASSDomEvents["hass-drawer-closed"]>;
|
||||
"hass-layout-transition": HASSDomEvent<
|
||||
HASSDomEvents["hass-layout-transition"]
|
||||
>;
|
||||
}
|
||||
}
|
||||
|
||||
const blockingElements = (document as any).$blockingElements;
|
||||
|
||||
@customElement("ha-drawer")
|
||||
export class HaDrawer extends LitElement {
|
||||
private static readonly _SWIPE_AXIS_TOLERANCE = 32;
|
||||
export class HaDrawer extends DrawerBase {
|
||||
@property() public direction: "ltr" | "rtl" = "ltr";
|
||||
|
||||
@property({ reflect: true }) public direction: "ltr" | "rtl" = "ltr";
|
||||
private _mc?: HammerManager;
|
||||
|
||||
@property({ reflect: true }) public type: "" | "dismissible" | "modal" = "";
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public open = false;
|
||||
|
||||
@query("wa-drawer") private _modalDrawer?: HTMLElement;
|
||||
|
||||
@query(".sidebar-shell") private _sidebarShell?: HTMLElement;
|
||||
private _rtlStyle?: HTMLElement;
|
||||
|
||||
private _sidebarTransitionActive = false;
|
||||
|
||||
private _transitionTarget?: HTMLElement;
|
||||
|
||||
private _gestureRecognizer = new SwipeGestureRecognizer({
|
||||
velocitySwipeThreshold: 0.35,
|
||||
});
|
||||
|
||||
private _touchStartY = 0;
|
||||
|
||||
private _touchDeltaY = 0;
|
||||
|
||||
private get _modal() {
|
||||
return this.type === "modal";
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return this._modal
|
||||
? html`
|
||||
<slot name="appContent"></slot>
|
||||
<wa-drawer
|
||||
placement="start"
|
||||
.open=${this.open}
|
||||
light-dismiss
|
||||
without-header
|
||||
@touchstart=${this._handleTouchStart}
|
||||
@wa-after-hide=${this._handleAfterHide}
|
||||
>
|
||||
<slot></slot>
|
||||
</wa-drawer>
|
||||
`
|
||||
: html`
|
||||
<div class="layout">
|
||||
<div class="sidebar-shell">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="app-content">
|
||||
<slot name="appContent"></slot>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected updated(_: PropertyValues<this>) {
|
||||
this._syncTransitionListeners();
|
||||
|
||||
if (!this.open) {
|
||||
this._resetSwipeTracking();
|
||||
}
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
this._syncTransitionListeners();
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._removeTransitionListeners();
|
||||
this._unregisterSwipeHandlers();
|
||||
}
|
||||
|
||||
private _handleAfterHide(ev: Event) {
|
||||
ev.stopPropagation();
|
||||
this.open = false;
|
||||
fireEvent(this, "hass-drawer-closed");
|
||||
}
|
||||
|
||||
private _closeModalDrawer() {
|
||||
this.open = false;
|
||||
}
|
||||
|
||||
private _handleDrawerTransitionStart = (ev: TransitionEvent) => {
|
||||
if (
|
||||
ev.propertyName !==
|
||||
(this.type === "dismissible" ? "transform" : "width") ||
|
||||
this._sidebarTransitionActive
|
||||
) {
|
||||
if (ev.propertyName !== "width" || this._sidebarTransitionActive) {
|
||||
return;
|
||||
}
|
||||
this._sidebarTransitionActive = true;
|
||||
@@ -120,11 +41,7 @@ export class HaDrawer extends LitElement {
|
||||
};
|
||||
|
||||
private _handleDrawerTransitionEnd = (ev: TransitionEvent) => {
|
||||
if (
|
||||
ev.propertyName !==
|
||||
(this.type === "dismissible" ? "transform" : "width") ||
|
||||
!this._sidebarTransitionActive
|
||||
) {
|
||||
if (ev.propertyName !== "width" || !this._sidebarTransitionActive) {
|
||||
return;
|
||||
}
|
||||
this._sidebarTransitionActive = false;
|
||||
@@ -134,207 +51,150 @@ export class HaDrawer extends LitElement {
|
||||
});
|
||||
};
|
||||
|
||||
private _handleTouchStart = (ev: TouchEvent) => {
|
||||
if (!this._modal || !this.open) {
|
||||
return;
|
||||
}
|
||||
|
||||
const drawer = this._modalDrawer;
|
||||
const dialog = drawer?.shadowRoot?.querySelector(
|
||||
"dialog"
|
||||
) as HTMLDialogElement | null;
|
||||
|
||||
if (!dialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
const path = ev.composedPath();
|
||||
|
||||
if (!path.includes(dialog)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ev.stopPropagation();
|
||||
this._startSwipeTracking(ev.touches[0].clientX, ev.touches[0].clientY);
|
||||
};
|
||||
|
||||
private _startSwipeTracking(clientX: number, clientY: number) {
|
||||
document.addEventListener("touchmove", this._handleTouchMove, {
|
||||
passive: true,
|
||||
});
|
||||
document.addEventListener("touchend", this._handleTouchEnd);
|
||||
document.addEventListener("touchcancel", this._handleTouchEnd);
|
||||
|
||||
this._touchStartY = clientY;
|
||||
this._touchDeltaY = 0;
|
||||
this._gestureRecognizer.start(clientX);
|
||||
protected createAdapter() {
|
||||
return {
|
||||
...super.createAdapter(),
|
||||
trapFocus: () => {
|
||||
blockingElements.push(this);
|
||||
this.appContent.inert = true;
|
||||
document.body.style.overflow = "hidden";
|
||||
},
|
||||
releaseFocus: () => {
|
||||
blockingElements.remove(this);
|
||||
this.appContent.inert = false;
|
||||
document.body.style.overflow = "";
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private _handleTouchMove = (ev: TouchEvent) => {
|
||||
const currentX = ev.touches[0].clientX;
|
||||
const currentY = ev.touches[0].clientY;
|
||||
this._touchDeltaY = Math.abs(currentY - this._touchStartY);
|
||||
this._gestureRecognizer.move(currentX);
|
||||
};
|
||||
protected updated(changedProps: PropertyValues<this>) {
|
||||
super.updated(changedProps);
|
||||
if (changedProps.has("direction")) {
|
||||
this.mdcRoot.dir = this.direction;
|
||||
if (this.direction === "rtl") {
|
||||
this._rtlStyle = document.createElement("style");
|
||||
this._rtlStyle.innerHTML = `
|
||||
.mdc-drawer--animate {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
.mdc-drawer--opening {
|
||||
transform: translateX(0);
|
||||
}
|
||||
.mdc-drawer--closing {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
`;
|
||||
|
||||
private _handleTouchEnd = () => {
|
||||
this._unregisterSwipeHandlers();
|
||||
|
||||
const result = this._gestureRecognizer.end();
|
||||
const isHorizontalGesture =
|
||||
Math.abs(result.delta) >
|
||||
this._touchDeltaY + HaDrawer._SWIPE_AXIS_TOLERANCE;
|
||||
|
||||
if (!isHorizontalGesture) {
|
||||
this._resetSwipeTracking();
|
||||
return;
|
||||
}
|
||||
|
||||
const drawerDialog = this._modalDrawer?.shadowRoot?.querySelector(
|
||||
'[part="dialog"]'
|
||||
) as HTMLElement | null;
|
||||
const drawerWidth = drawerDialog?.offsetWidth || 0;
|
||||
|
||||
if (result.isSwipe) {
|
||||
const closeByVelocity =
|
||||
this.direction === "rtl"
|
||||
? result.isDownwardSwipe
|
||||
: !result.isDownwardSwipe;
|
||||
|
||||
if (closeByVelocity) {
|
||||
this._closeModalDrawer();
|
||||
this.shadowRoot!.appendChild(this._rtlStyle);
|
||||
} else if (this._rtlStyle) {
|
||||
this.shadowRoot!.removeChild(this._rtlStyle);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const closeByDistance =
|
||||
drawerWidth > 0 &&
|
||||
(this.direction === "rtl"
|
||||
? result.delta > 0 && Math.abs(result.delta) > drawerWidth * 0.5
|
||||
: result.delta < 0 && Math.abs(result.delta) > drawerWidth * 0.5);
|
||||
|
||||
if (closeByDistance) {
|
||||
this._closeModalDrawer();
|
||||
if (changedProps.has("open") && this.open && this.type === "modal") {
|
||||
this._setupSwipe();
|
||||
} else if (this._mc) {
|
||||
this._mc.destroy();
|
||||
this._mc = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
private _unregisterSwipeHandlers() {
|
||||
document.removeEventListener("touchmove", this._handleTouchMove);
|
||||
document.removeEventListener("touchend", this._handleTouchEnd);
|
||||
document.removeEventListener("touchcancel", this._handleTouchEnd);
|
||||
}
|
||||
|
||||
private _resetSwipeTracking() {
|
||||
this._unregisterSwipeHandlers();
|
||||
this._gestureRecognizer.reset();
|
||||
this._touchStartY = 0;
|
||||
this._touchDeltaY = 0;
|
||||
}
|
||||
|
||||
private _syncTransitionListeners() {
|
||||
if (this._transitionTarget === this._sidebarShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._removeTransitionListeners();
|
||||
|
||||
if (!this._sidebarShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._transitionTarget = this._sidebarShell;
|
||||
this._transitionTarget.addEventListener(
|
||||
protected firstUpdated() {
|
||||
super.firstUpdated();
|
||||
this.mdcRoot?.addEventListener(
|
||||
"transitionstart",
|
||||
this._handleDrawerTransitionStart
|
||||
);
|
||||
this._transitionTarget.addEventListener(
|
||||
this.mdcRoot?.addEventListener(
|
||||
"transitionend",
|
||||
this._handleDrawerTransitionEnd
|
||||
);
|
||||
this._transitionTarget.addEventListener(
|
||||
this.mdcRoot?.addEventListener(
|
||||
"transitioncancel",
|
||||
this._handleDrawerTransitionEnd
|
||||
);
|
||||
}
|
||||
|
||||
private _removeTransitionListeners() {
|
||||
if (!this._transitionTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._transitionTarget.removeEventListener(
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.mdcRoot?.removeEventListener(
|
||||
"transitionstart",
|
||||
this._handleDrawerTransitionStart
|
||||
);
|
||||
this._transitionTarget.removeEventListener(
|
||||
this.mdcRoot?.removeEventListener(
|
||||
"transitionend",
|
||||
this._handleDrawerTransitionEnd
|
||||
);
|
||||
this._transitionTarget.removeEventListener(
|
||||
this.mdcRoot?.removeEventListener(
|
||||
"transitioncancel",
|
||||
this._handleDrawerTransitionEnd
|
||||
);
|
||||
this._transitionTarget = undefined;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
private async _setupSwipe() {
|
||||
const hammer = await import("../resources/hammer");
|
||||
this._mc = new hammer.Manager(document, {
|
||||
touchAction: "pan-y",
|
||||
});
|
||||
this._mc.add(
|
||||
new hammer.Swipe({
|
||||
direction:
|
||||
this.direction === "rtl"
|
||||
? hammer.DIRECTION_RIGHT
|
||||
: hammer.DIRECTION_LEFT,
|
||||
})
|
||||
);
|
||||
this._mc.on("swipeleft swiperight", () => {
|
||||
fireEvent(this, "hass-toggle-menu", { open: false });
|
||||
});
|
||||
}
|
||||
|
||||
.layout {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sidebar-shell {
|
||||
position: fixed;
|
||||
width: var(--ha-sidebar-width);
|
||||
height: 100%;
|
||||
border-inline-end: 1px solid var(--divider-color, rgba(0, 0, 0, 0.12));
|
||||
box-sizing: border-box;
|
||||
transition: width var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
|
||||
.app-content {
|
||||
overflow: unset;
|
||||
min-width: 0;
|
||||
padding-inline-start: var(--ha-sidebar-width);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
transition:
|
||||
padding-inline-start var(--ha-animation-duration-normal) ease,
|
||||
width var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
|
||||
:host([type="dismissible"]) .sidebar-shell {
|
||||
transition: transform var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
|
||||
:host([type="dismissible"]:not([open])) .sidebar-shell {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
:host([type="dismissible"][direction="rtl"]:not([open])) .sidebar-shell {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
:host([type="dismissible"]:not([open])) .app-content {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
wa-drawer {
|
||||
--size: var(--ha-sidebar-width, 256px);
|
||||
--show-duration: var(--ha-animation-duration-normal);
|
||||
--hide-duration: var(--ha-animation-duration-normal);
|
||||
}
|
||||
|
||||
wa-drawer::part(body) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
static override styles = [
|
||||
styles,
|
||||
css`
|
||||
.mdc-drawer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
border-color: var(--divider-color, rgba(0, 0, 0, 0.12));
|
||||
inset-inline-start: 0 !important;
|
||||
inset-inline-end: initial !important;
|
||||
transition-property: transform, width;
|
||||
transition-duration:
|
||||
var(--mdc-drawer-transition-duration, 0.2s),
|
||||
var(--ha-animation-duration-normal);
|
||||
transition-timing-function:
|
||||
var(
|
||||
--mdc-drawer-transition-timing-function,
|
||||
cubic-bezier(0.4, 0, 0.2, 1)
|
||||
),
|
||||
ease;
|
||||
}
|
||||
.mdc-drawer.mdc-drawer--modal.mdc-drawer--open {
|
||||
z-index: 200;
|
||||
}
|
||||
.mdc-drawer-app-content {
|
||||
overflow: unset;
|
||||
flex: none;
|
||||
padding-left: var(--mdc-drawer-width);
|
||||
padding-inline-start: var(--mdc-drawer-width);
|
||||
padding-inline-end: initial;
|
||||
direction: var(--direction);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
transition:
|
||||
padding-left var(--ha-animation-duration-normal) ease,
|
||||
padding-inline-start var(--ha-animation-duration-normal) ease;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
/* Use 1ms instead of "none" so the transitionend event still fires.
|
||||
The MDC drawer foundation relies on it to complete the close cycle. */
|
||||
.mdc-drawer,
|
||||
.mdc-drawer-app-content {
|
||||
transition: 1ms;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { SelectedDetail } from "@material/mwc-list";
|
||||
import { mdiFilterVariantRemove } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { deepEqual } from "../common/util/deep-equal";
|
||||
import type { Blueprints } from "../data/blueprint";
|
||||
@@ -32,8 +32,6 @@ export class HaFilterBlueprints extends LitElement {
|
||||
|
||||
@state() private _blueprints?: Blueprints;
|
||||
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
public willUpdate(properties: PropertyValues<this>) {
|
||||
super.willUpdate(properties);
|
||||
|
||||
@@ -98,7 +96,8 @@ export class HaFilterBlueprints extends LitElement {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (this.narrow || !this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - 49}px`;
|
||||
this.renderRoot.querySelector("ha-list")!.style.height =
|
||||
`${this.clientHeight - 49}px`;
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import type { CategoryRegistryEntry } from "../data/category_registry";
|
||||
@@ -49,8 +49,6 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) {
|
||||
|
||||
@state() private _shouldRender = false;
|
||||
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
protected hassSubscribeRequiredHostProps = ["scope"];
|
||||
|
||||
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
||||
@@ -171,7 +169,8 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (!this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - (49 + 48)}px`;
|
||||
this.renderRoot.querySelector("ha-list")!.style.height =
|
||||
`${this.clientHeight - (49 + 48)}px`;
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiFilterVariantRemove } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDeviceNameDisplay } from "../common/entity/compute_device_name";
|
||||
@@ -34,8 +34,6 @@ export class HaFilterDevices extends LitElement {
|
||||
|
||||
@state() private _filter?: string;
|
||||
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
public willUpdate(properties: PropertyValues<this>) {
|
||||
super.willUpdate(properties);
|
||||
|
||||
@@ -137,7 +135,8 @@ export class HaFilterDevices extends LitElement {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (!this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - 49 - 4 - 32}px`;
|
||||
this.renderRoot.querySelector("ha-list")!.style.height =
|
||||
`${this.clientHeight - 49 - 4 - 32}px`;
|
||||
// 49px - height of a header + 1px
|
||||
// 4px - padding-top of the search-input
|
||||
// 32px - height of the search input
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { SelectedDetail } from "@material/mwc-list";
|
||||
import { mdiFilterVariantRemove } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
@@ -32,8 +31,6 @@ export class HaFilterDomains extends LitElement {
|
||||
|
||||
@state() private _filter?: string;
|
||||
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-expansion-panel
|
||||
@@ -65,7 +62,7 @@ export class HaFilterDomains extends LitElement {
|
||||
multi
|
||||
>
|
||||
${repeat(
|
||||
this._domains(this.hass.states, this._filter, this.value),
|
||||
this._domains(this.hass.states, this._filter),
|
||||
(i) => i,
|
||||
(domain) =>
|
||||
html`<ha-check-list-item
|
||||
@@ -87,7 +84,7 @@ export class HaFilterDomains extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _domains = memoizeOne((states, filter, _value) => {
|
||||
private _domains = memoizeOne((states, filter) => {
|
||||
const domains = new Set<string>();
|
||||
Object.keys(states).forEach((entityId) => {
|
||||
domains.add(computeDomain(entityId));
|
||||
@@ -112,7 +109,8 @@ export class HaFilterDomains extends LitElement {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (!this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - 49 - 4 - 32}px`;
|
||||
this.renderRoot.querySelector("ha-list")!.style.height =
|
||||
`${this.clientHeight - 49 - 4 - 32}px`;
|
||||
// 49px - height of a header + 1px
|
||||
// 4px - padding-top of the search-input
|
||||
// 32px - height of the search input
|
||||
@@ -128,19 +126,19 @@ export class HaFilterDomains extends LitElement {
|
||||
this.expanded = ev.detail.expanded;
|
||||
}
|
||||
|
||||
private _handleItemSelected(ev: CustomEvent<SelectedDetail<Set<number>>>) {
|
||||
const domains = this._domains(this.hass.states, this._filter, this.value);
|
||||
|
||||
const visibleDomains = new Set(domains);
|
||||
const preserved = (this.value || []).filter((d) => !visibleDomains.has(d));
|
||||
const selected = [...ev.detail.index]
|
||||
.map((i) => domains[i])
|
||||
.filter((d): d is string => !!d);
|
||||
|
||||
this.value = [...preserved, ...selected];
|
||||
private _handleItemSelected(
|
||||
ev: CustomEvent<{ diff: { added: number[]; removed: number[] } }>
|
||||
) {
|
||||
const domains = this._domains(this.hass.states, this._filter);
|
||||
if (ev.detail.diff.added.length) {
|
||||
this.value = [...(this.value || []), domains[ev.detail.diff.added[0]]];
|
||||
} else if (ev.detail.diff.removed.length) {
|
||||
const removedDomain = domains[ev.detail.diff.removed[0]];
|
||||
this.value = this.value?.filter((value) => value !== removedDomain);
|
||||
}
|
||||
|
||||
fireEvent(this, "data-table-filter-changed", {
|
||||
value: this.value.length ? this.value : undefined,
|
||||
value: this.value,
|
||||
items: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiFilterVariantRemove } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeStateDomain } from "../common/entity/compute_state_domain";
|
||||
@@ -36,8 +36,6 @@ export class HaFilterEntities extends LitElement {
|
||||
|
||||
@state() private _filter?: string;
|
||||
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
public willUpdate(properties: PropertyValues<this>) {
|
||||
super.willUpdate(properties);
|
||||
|
||||
@@ -104,7 +102,8 @@ export class HaFilterEntities extends LitElement {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (!this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - 49 - 4 - 32}px`;
|
||||
this.renderRoot.querySelector("ha-list")!.style.height =
|
||||
`${this.clientHeight - 49 - 4 - 32}px`;
|
||||
// 49px - height of a header + 1px
|
||||
// 4px - padding-top of the search-input
|
||||
// 32px - height of the search input
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiFilterVariantRemove, mdiTextureBox } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -42,8 +42,6 @@ export class HaFilterFloorAreas extends LitElement {
|
||||
|
||||
@state() private _shouldRender = false;
|
||||
|
||||
@query("ha-list-selectable") private _list?: HTMLElement;
|
||||
|
||||
public willUpdate(properties: PropertyValues<this>) {
|
||||
super.willUpdate(properties);
|
||||
|
||||
@@ -209,7 +207,8 @@ export class HaFilterFloorAreas extends LitElement {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (!this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - 49}px`;
|
||||
this.renderRoot.querySelector("ha-list-selectable")!.style.height =
|
||||
`${this.clientHeight - 49}px`;
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { SelectedDetail } from "@material/mwc-list";
|
||||
import { mdiFilterVariantRemove } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
@@ -35,8 +34,6 @@ export class HaFilterIntegrations extends LitElement {
|
||||
|
||||
@state() private _filter?: string;
|
||||
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-expansion-panel
|
||||
@@ -101,7 +98,8 @@ export class HaFilterIntegrations extends LitElement {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (!this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - 49 - 4 - 32}px`;
|
||||
this.renderRoot.querySelector("ha-list")!.style.height =
|
||||
`${this.clientHeight - 49 - 4 - 32}px`;
|
||||
// 49px - height of a header + 1px
|
||||
// 4px - padding-top of the search-input
|
||||
// 32px - height of the search input
|
||||
@@ -149,7 +147,9 @@ export class HaFilterIntegrations extends LitElement {
|
||||
)
|
||||
);
|
||||
|
||||
private _itemSelected(ev: CustomEvent<SelectedDetail<Set<number>>>) {
|
||||
private _itemSelected(
|
||||
ev: CustomEvent<{ diff: { added: number[]; removed: number[] } }>
|
||||
) {
|
||||
const integrations = this._integrations(
|
||||
this.hass.localize,
|
||||
this._manifests!,
|
||||
@@ -157,16 +157,18 @@ export class HaFilterIntegrations extends LitElement {
|
||||
this.value
|
||||
);
|
||||
|
||||
const visibleDomains = new Set(integrations.map((i) => i.domain));
|
||||
const preserved = (this.value || []).filter((d) => !visibleDomains.has(d));
|
||||
const selected = [...ev.detail.index]
|
||||
.map((i) => integrations[i]?.domain)
|
||||
.filter((d): d is string => !!d);
|
||||
|
||||
this.value = [...preserved, ...selected];
|
||||
if (ev.detail.diff.added.length) {
|
||||
this.value = [
|
||||
...(this.value || []),
|
||||
integrations[ev.detail.diff.added[0]].domain,
|
||||
];
|
||||
} else if (ev.detail.diff.removed.length) {
|
||||
const removedDomain = integrations[ev.detail.diff.removed[0]].domain;
|
||||
this.value = this.value?.filter((val) => val !== removedDomain);
|
||||
}
|
||||
|
||||
fireEvent(this, "data-table-filter-changed", {
|
||||
value: this.value.length ? this.value : undefined,
|
||||
value: this.value,
|
||||
items: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { SelectedDetail } from "@material/mwc-list";
|
||||
import { mdiCog, mdiFilterVariantRemove } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
@@ -41,8 +41,6 @@ export class HaFilterLabels extends LitElement {
|
||||
|
||||
@state() private _filter?: string;
|
||||
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
private _filteredLabels = memoizeOne(
|
||||
// `_value` used to recalculate the memoization when the selection changes
|
||||
(labels: LabelRegistryEntry[], filter: string | undefined, _value) =>
|
||||
@@ -139,7 +137,8 @@ export class HaFilterLabels extends LitElement {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (!this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - (49 + 48 + 32 + 4)}px`;
|
||||
this.renderRoot.querySelector("ha-list")!.style.height =
|
||||
`${this.clientHeight - (49 + 48 + 32 + 4)}px`;
|
||||
// 49px - height of a header + 1px
|
||||
// 4px - padding-top of the search-input
|
||||
// 32px - height of the search input
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { SelectedDetail } from "@material/mwc-list";
|
||||
import { mdiFilterVariantRemove } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
@@ -33,8 +33,6 @@ export class HaFilterVoiceAssistants extends LitElement {
|
||||
|
||||
@state() private _shouldRender = false;
|
||||
|
||||
@query("ha-list") private _list?: HTMLElement;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-expansion-panel
|
||||
@@ -95,7 +93,8 @@ export class HaFilterVoiceAssistants extends LitElement {
|
||||
if (changed.has("expanded") && this.expanded) {
|
||||
setTimeout(() => {
|
||||
if (!this.expanded) return;
|
||||
this._list!.style.height = `${this.clientHeight - 49}px`;
|
||||
this.renderRoot.querySelector("ha-list")!.style.height =
|
||||
`${this.clientHeight - 49}px`;
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiPlus } from "@mdi/js";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
@@ -49,15 +49,14 @@ export class HaFormOptionalActions extends LitElement implements HaFormElement {
|
||||
|
||||
@state() private _displayActions?: string[];
|
||||
|
||||
@query("ha-form") private _form?: HaForm;
|
||||
|
||||
public async focus() {
|
||||
await this.updateComplete;
|
||||
this._form?.focus();
|
||||
this.renderRoot.querySelector("ha-form")?.focus();
|
||||
}
|
||||
|
||||
public reportValidity(): boolean {
|
||||
return this._form ? this._form.reportValidity() : true;
|
||||
const form = this.renderRoot.querySelector<HaForm>("ha-form");
|
||||
return form ? form.reportValidity() : true;
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues<this>): void {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
@@ -83,10 +83,8 @@ export class HaForm extends LitElement implements HaFormElement {
|
||||
delegatesFocus: true,
|
||||
};
|
||||
|
||||
@query(".root") private _root?: HTMLElement;
|
||||
|
||||
public reportValidity(): boolean {
|
||||
const root = this._root;
|
||||
const root = this.renderRoot.querySelector(".root");
|
||||
if (!root) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -314,7 +314,6 @@ export class HaItemDisplayEditor extends LitElement {
|
||||
// refocus the item after the sort
|
||||
setTimeout(async () => {
|
||||
await this.updateComplete;
|
||||
// eslint-disable-next-line lit/prefer-query-decorators
|
||||
const selectedElement = this.shadowRoot?.querySelector(
|
||||
`ha-md-list-item:nth-child(${this._dragIndex! + 1})`
|
||||
) as HTMLElement | null;
|
||||
|
||||
@@ -188,6 +188,7 @@ export class HaObjectSelector extends LitElement {
|
||||
}
|
||||
|
||||
return html`<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.readonly=${this.disabled}
|
||||
.label=${this.label}
|
||||
.required=${this.required}
|
||||
|
||||
@@ -101,6 +101,7 @@ export class HaTemplateSelector extends LitElement {
|
||||
: nothing}
|
||||
<ha-code-editor
|
||||
mode="jinja2"
|
||||
.hass=${this.hass}
|
||||
.value=${this.value}
|
||||
.readOnly=${this.disabled}
|
||||
.placeholder=${this.placeholder || "{{ ... }}"}
|
||||
|
||||
@@ -86,6 +86,9 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: "show-advanced", type: Boolean })
|
||||
public showAdvanced = false;
|
||||
|
||||
@property({ attribute: "show-service-id", type: Boolean })
|
||||
public showServiceId = false;
|
||||
|
||||
@@ -542,6 +545,7 @@ export class HaServiceControl extends LitElement {
|
||||
: ""}
|
||||
${shouldRenderServiceDataYaml
|
||||
? html`<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.service-control.action_data"
|
||||
)}
|
||||
@@ -663,7 +667,10 @@ export class HaServiceControl extends LitElement {
|
||||
? this.hass.services[domain][serviceName].description_placeholders
|
||||
: undefined;
|
||||
|
||||
return dataField.selector
|
||||
return dataField.selector &&
|
||||
(!dataField.advanced ||
|
||||
this.showAdvanced ||
|
||||
(this._value?.data && this._value.data[dataField.key] !== undefined))
|
||||
? html`<ha-settings-row .narrow=${this.narrow}>
|
||||
${!showOptional
|
||||
? hasOptional
|
||||
|
||||
@@ -2,7 +2,7 @@ import { mdiStarFourPoints } from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type {
|
||||
@@ -52,10 +52,6 @@ export class HaSuggestWithAIButton extends LitElement {
|
||||
@state()
|
||||
private _minWidth?: string;
|
||||
|
||||
@query("ha-assist-chip") private _chip?: HTMLElement & {
|
||||
offsetWidth: number;
|
||||
};
|
||||
|
||||
private _intervalId?: number;
|
||||
|
||||
protected firstUpdated(_changedProperties: PropertyValues<this>): void {
|
||||
@@ -113,8 +109,9 @@ export class HaSuggestWithAIButton extends LitElement {
|
||||
}
|
||||
|
||||
// Capture current width before changing state
|
||||
if (this._chip) {
|
||||
this._minWidth = `${this._chip.offsetWidth}px`;
|
||||
const chip = this.shadowRoot?.querySelector("ha-assist-chip");
|
||||
if (chip) {
|
||||
this._minWidth = `${chip.offsetWidth}px`;
|
||||
}
|
||||
|
||||
// Reset to suggesting state
|
||||
|
||||
@@ -486,7 +486,6 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
|
||||
fireEvent(this, "value-changed", { value });
|
||||
|
||||
// eslint-disable-next-line lit/prefer-query-decorators
|
||||
this.shadowRoot
|
||||
?.querySelector(
|
||||
`ha-target-picker-item-group[type='${this._newTarget?.type}']`
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import "@home-assistant/webawesome/dist/components/popup/popup";
|
||||
import { mdiLightbulbOutline } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
@@ -9,18 +10,41 @@ import "./ha-svg-icon";
|
||||
class HaTip extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
/**
|
||||
* When set, renders the tip inside a popup anchored to the given element
|
||||
* instead of inline. Does not steal focus.
|
||||
*/
|
||||
@property({ attribute: false }) public popoverAnchor?: Element;
|
||||
|
||||
public render() {
|
||||
if (!this.hass) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
const content = html`
|
||||
<ha-svg-icon .path=${mdiLightbulbOutline}></ha-svg-icon>
|
||||
<span class="prefix"
|
||||
>${this.hass.localize("ui.panel.config.tips.tip")}</span
|
||||
>
|
||||
<span class="text"><slot></slot></span>
|
||||
`;
|
||||
|
||||
if (this.popoverAnchor) {
|
||||
return html`
|
||||
<wa-popup
|
||||
active
|
||||
.anchor=${this.popoverAnchor}
|
||||
placement="top-start"
|
||||
distance="4"
|
||||
flip
|
||||
shift
|
||||
>
|
||||
<div class="popup-content">${content}</div>
|
||||
</wa-popup>
|
||||
`;
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
@@ -40,6 +64,14 @@ class HaTip extends LitElement {
|
||||
.prefix {
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
|
||||
.popup-content {
|
||||
padding: var(--ha-space-2) var(--ha-space-3);
|
||||
background: var(--card-background-color);
|
||||
border-radius: var(--ha-border-radius-xl);
|
||||
box-shadow: var(--wa-shadow-m);
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,14 @@ import { DEFAULT_SCHEMA, dump, load } from "js-yaml";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import type { ContextType } from "@lit/context";
|
||||
import { consume } from "@lit/context";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { copyToClipboard } from "../common/util/copy-clipboard";
|
||||
import { haStyle } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { showToast } from "../util/toast";
|
||||
import "./ha-button";
|
||||
import "./ha-code-editor";
|
||||
import type { HaCodeEditor } from "./ha-code-editor";
|
||||
import { internationalizationContext } from "../data/context";
|
||||
|
||||
const isEmpty = (obj: Record<string, unknown>): boolean => {
|
||||
if (typeof obj !== "object" || obj === null) {
|
||||
@@ -28,6 +26,8 @@ const isEmpty = (obj: Record<string, unknown>): boolean => {
|
||||
|
||||
@customElement("ha-yaml-editor")
|
||||
export class HaYamlEditor extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public value?: any;
|
||||
|
||||
@property({ attribute: false }) public yamlSchema: Schema = DEFAULT_SCHEMA;
|
||||
@@ -59,10 +59,6 @@ export class HaYamlEditor extends LitElement {
|
||||
|
||||
@state() private _yaml = "";
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n?: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@query("ha-code-editor") _codeEditor?: HaCodeEditor;
|
||||
|
||||
public setValue(value): void {
|
||||
@@ -116,6 +112,7 @@ export class HaYamlEditor extends LitElement {
|
||||
? html`<p>${this.label}${this.required ? " *" : ""}</p>`
|
||||
: nothing}
|
||||
<ha-code-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this._yaml}
|
||||
.readOnly=${this.readOnly}
|
||||
.disableFullscreen=${this.disableFullscreen}
|
||||
@@ -135,7 +132,7 @@ export class HaYamlEditor extends LitElement {
|
||||
${this.copyClipboard
|
||||
? html`
|
||||
<ha-button appearance="plain" @click=${this._copyYaml}>
|
||||
${this._i18n!.localize(
|
||||
${this.hass.localize(
|
||||
"ui.components.yaml-editor.copy_to_clipboard"
|
||||
)}
|
||||
</ha-button>
|
||||
@@ -166,7 +163,7 @@ export class HaYamlEditor extends LitElement {
|
||||
// Invalid YAML
|
||||
isValid = false;
|
||||
yamlError = err;
|
||||
errorMsg = `${this._i18n!.localize("ui.components.yaml-editor.error", { reason: err.reason })}${err.mark ? ` (${this._i18n!.localize("ui.components.yaml-editor.error_location", { line: err.mark.line + 1, column: err.mark.column + 1 })})` : ""}`;
|
||||
errorMsg = `${this.hass.localize("ui.components.yaml-editor.error", { reason: err.reason })}${err.mark ? ` (${this.hass.localize("ui.components.yaml-editor.error_location", { line: err.mark.line + 1, column: err.mark.column + 1 })})` : ""}`;
|
||||
}
|
||||
} else {
|
||||
parsed = {};
|
||||
@@ -204,7 +201,7 @@ export class HaYamlEditor extends LitElement {
|
||||
if (this.yaml) {
|
||||
await copyToClipboard(this.yaml);
|
||||
showToast(this, {
|
||||
message: this._i18n!.localize("ui.common.copied_clipboard"),
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiDeleteOutline, mdiDragHorizontalVariant, mdiPlus } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { internationalizationContext } from "../../data/context";
|
||||
@@ -67,8 +67,6 @@ class HaInputMulti extends LitElement {
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n?: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@query("ha-input[data-last]") private _lastInput?: HaInput;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-sortable
|
||||
@@ -165,7 +163,10 @@ class HaInputMulti extends LitElement {
|
||||
const items = [...this._items, ""];
|
||||
this._fireChanged(items);
|
||||
await this.updateComplete;
|
||||
this._lastInput?.focus();
|
||||
const field = this.shadowRoot?.querySelector(`ha-input[data-last]`) as
|
||||
| HaInput
|
||||
| undefined;
|
||||
field?.focus();
|
||||
}
|
||||
|
||||
private async _editItem(ev: Event) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import "../ha-ripple";
|
||||
import { HaListItemBase } from "./ha-list-item-base";
|
||||
@@ -34,10 +34,8 @@ export class HaListItemButton extends HaListItemBase {
|
||||
|
||||
@property({ type: String }) public download?: string;
|
||||
|
||||
@query("#item") private _item?: HTMLElement;
|
||||
|
||||
public override activate(): void {
|
||||
this._item?.click();
|
||||
this.renderRoot.querySelector<HTMLElement>("#item")?.click();
|
||||
}
|
||||
|
||||
protected _renderBase(inner: TemplateResult): TemplateResult {
|
||||
|
||||
@@ -130,6 +130,10 @@ export class HaRowItem extends LitElement {
|
||||
color: var(--primary-text-color);
|
||||
font-size: var(--ha-font-size-m);
|
||||
line-height: var(--ha-line-height-normal);
|
||||
--ha-row-item-padding-block: var(--ha-space-3);
|
||||
--ha-row-item-padding-inline: var(--ha-space-4);
|
||||
--ha-row-item-gap: var(--ha-space-4);
|
||||
--ha-row-item-min-height: 48px;
|
||||
}
|
||||
:host([disabled]) {
|
||||
color: var(--disabled-text-color);
|
||||
@@ -140,10 +144,10 @@ export class HaRowItem extends LitElement {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--ha-row-item-gap, var(--ha-space-4));
|
||||
padding-block: var(--ha-row-item-padding-block, var(--ha-space-3));
|
||||
padding-inline: var(--ha-row-item-padding-inline, var(--ha-space-4));
|
||||
min-height: var(--ha-row-item-min-height, 48px);
|
||||
gap: var(--ha-row-item-gap);
|
||||
padding-block: var(--ha-row-item-padding-block);
|
||||
padding-inline: var(--ha-row-item-padding-inline);
|
||||
min-height: var(--ha-row-item-min-height);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.content {
|
||||
|
||||
@@ -292,12 +292,14 @@ export class HaListBase extends LitElement {
|
||||
static styles: CSSResultGroup = css`
|
||||
:host {
|
||||
display: block;
|
||||
--ha-list-gap: 0;
|
||||
--ha-list-padding: 0;
|
||||
}
|
||||
.base {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ha-list-gap, 0);
|
||||
padding: var(--ha-list-padding, 0);
|
||||
gap: var(--ha-list-gap);
|
||||
padding: var(--ha-list-padding);
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@@ -121,15 +121,15 @@ export class HaListSelectable extends HaListBase {
|
||||
|
||||
public updateListItems() {
|
||||
super.updateListItems();
|
||||
this._syncItemSelectedState(true);
|
||||
this._syncItemSelectedState();
|
||||
}
|
||||
|
||||
private _sortedSelectedIndices(): number[] {
|
||||
return [...this._selectedIndices!].sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
private _syncItemSelectedState(reset = false): void {
|
||||
if (!this._selectedIndices || reset) {
|
||||
private _syncItemSelectedState() {
|
||||
if (!this._selectedIndices) {
|
||||
this._selectedIndices = new Set<number>();
|
||||
this.items.forEach((item, i) => {
|
||||
const opt = item as HaListItemOption;
|
||||
|
||||
@@ -12,7 +12,7 @@ import type {
|
||||
} from "leaflet";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, ReactiveElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { formatDateTime } from "../../common/datetime/format_date_time";
|
||||
import {
|
||||
formatTimeWeekday,
|
||||
@@ -105,8 +105,6 @@ export class HaMap extends ReactiveElement {
|
||||
|
||||
@state() private _loaded = false;
|
||||
|
||||
@query("#map") private _mapElement?: HTMLElement;
|
||||
|
||||
public leafletMap?: Map;
|
||||
|
||||
private Leaflet?: LeafletModuleType;
|
||||
@@ -237,11 +235,11 @@ export class HaMap extends ReactiveElement {
|
||||
}
|
||||
|
||||
private _updateMapStyle(): void {
|
||||
const map = this._mapElement!;
|
||||
map.classList.toggle("clickable", this.clickable);
|
||||
map.classList.toggle("dark", this._darkMode);
|
||||
map.classList.toggle("forced-dark", this.themeMode === "dark");
|
||||
map.classList.toggle("forced-light", this.themeMode === "light");
|
||||
const map = this.renderRoot.querySelector("#map");
|
||||
map!.classList.toggle("clickable", this.clickable);
|
||||
map!.classList.toggle("dark", this._darkMode);
|
||||
map!.classList.toggle("forced-dark", this.themeMode === "dark");
|
||||
map!.classList.toggle("forced-light", this.themeMode === "light");
|
||||
}
|
||||
|
||||
private _loading = false;
|
||||
|
||||
@@ -22,6 +22,8 @@ import "../../ha-adaptive-dialog";
|
||||
import "../../ha-dialog-header";
|
||||
import "../../ha-icon-button";
|
||||
import "../../ha-icon-next";
|
||||
import "../../ha-md-list";
|
||||
import "../../ha-md-list-item";
|
||||
import "../../ha-svg-icon";
|
||||
import "../../list/ha-list-base";
|
||||
import "../ha-target-picker-item-row";
|
||||
|
||||
@@ -28,6 +28,8 @@ import "../ha-domain-icon";
|
||||
import { floorDefaultIconPath } from "../ha-floor-icon";
|
||||
import "../ha-icon";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-md-list";
|
||||
import "../ha-md-list-item";
|
||||
import "../ha-state-icon";
|
||||
import "../ha-tooltip";
|
||||
|
||||
|
||||
@@ -5,15 +5,19 @@ import { customElement, property } from "lit/decorators";
|
||||
import "../ha-code-editor";
|
||||
import "../ha-icon-button";
|
||||
import type { TraceExtended } from "../../data/trace";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
|
||||
@customElement("ha-trace-blueprint-config")
|
||||
export class HaTraceBlueprintConfig extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public trace!: TraceExtended;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-code-editor
|
||||
.value=${dump(this.trace.blueprint_inputs || "").trimRight()}
|
||||
.hass=${this.hass}
|
||||
read-only
|
||||
dir="ltr"
|
||||
></ha-code-editor>
|
||||
|
||||
@@ -5,15 +5,19 @@ import { customElement, property } from "lit/decorators";
|
||||
import "../ha-code-editor";
|
||||
import "../ha-icon-button";
|
||||
import type { TraceExtended } from "../../data/trace";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
|
||||
@customElement("ha-trace-config")
|
||||
export class HaTraceConfig extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public trace!: TraceExtended;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-code-editor
|
||||
.value=${dump(this.trace.config).trimRight()}
|
||||
.hass=${this.hass}
|
||||
read-only
|
||||
dir="ltr"
|
||||
></ha-code-editor>
|
||||
|
||||
@@ -271,6 +271,7 @@ export class HaTracePathDetails extends LitElement {
|
||||
return config
|
||||
? html`<ha-code-editor
|
||||
.value=${dump(config).trimEnd()}
|
||||
.hass=${this.hass}
|
||||
read-only
|
||||
dir="ltr"
|
||||
></ha-code-editor>`
|
||||
@@ -310,6 +311,7 @@ export class HaTracePathDetails extends LitElement {
|
||||
: html`<ha-code-editor
|
||||
read-only
|
||||
dir="ltr"
|
||||
.hass=${this.hass}
|
||||
.value=${dump(trace.changed_variables).trimEnd()}
|
||||
></ha-code-editor>`}
|
||||
`
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
} from "@mdi/js";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { Condition, Trigger } from "../../data/automation";
|
||||
@@ -73,9 +73,6 @@ export class HatScriptGraph extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public selected?: string;
|
||||
|
||||
@query("hat-graph-node[active], hat-graph-branch[active]")
|
||||
private _activeNode?: HTMLElement;
|
||||
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
public renderedNodes: Record<string, NodeInfo> = {};
|
||||
@@ -670,10 +667,12 @@ export class HatScriptGraph extends LitElement {
|
||||
|
||||
// Scroll to active node when selection changes
|
||||
if (changedProps.has("selected")) {
|
||||
this._activeNode?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
});
|
||||
const activeNode = this.renderRoot.querySelector(
|
||||
"hat-graph-node[active], hat-graph-branch[active]"
|
||||
) as HTMLElement;
|
||||
if (activeNode) {
|
||||
activeNode.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
||||
}
|
||||
}
|
||||
|
||||
if (!changedProps.has("trace")) {
|
||||
|
||||
@@ -5,7 +5,6 @@ import type {
|
||||
HomeAssistantApi,
|
||||
HomeAssistantConfig,
|
||||
HomeAssistantConnection,
|
||||
HomeAssistantFormatters,
|
||||
HomeAssistantInternationalization,
|
||||
HomeAssistantRegistries,
|
||||
HomeAssistantUI,
|
||||
@@ -64,14 +63,6 @@ export const uiContext = createContext<HomeAssistantUI>("hassUi");
|
||||
*/
|
||||
export const configContext = createContext<HomeAssistantConfig>("hassConfig");
|
||||
|
||||
/**
|
||||
* Entity formatting functions: `formatEntityState`, `formatEntityStateToParts`,
|
||||
* `formatEntityAttributeValue`, `formatEntityAttributeValueToParts`,
|
||||
* `formatEntityAttributeName`, and `formatEntityName`.
|
||||
*/
|
||||
export const formattersContext =
|
||||
createContext<HomeAssistantFormatters>("hassFormatters");
|
||||
|
||||
/**
|
||||
* Map of all entities in the entity registry, keyed by entity ID.
|
||||
*/
|
||||
|
||||
@@ -3,7 +3,6 @@ import type {
|
||||
HomeAssistantApi,
|
||||
HomeAssistantConfig,
|
||||
HomeAssistantConnection,
|
||||
HomeAssistantFormatters,
|
||||
HomeAssistantInternationalization,
|
||||
HomeAssistantRegistries,
|
||||
HomeAssistantUI,
|
||||
@@ -157,32 +156,6 @@ const updateConfig = (
|
||||
return value;
|
||||
};
|
||||
|
||||
const updateFormatters = (
|
||||
hass: HomeAssistant,
|
||||
value?: HomeAssistantFormatters
|
||||
): HomeAssistantFormatters => {
|
||||
if (
|
||||
!value ||
|
||||
value.formatEntityState !== hass.formatEntityState ||
|
||||
value.formatEntityStateToParts !== hass.formatEntityStateToParts ||
|
||||
value.formatEntityAttributeValue !== hass.formatEntityAttributeValue ||
|
||||
value.formatEntityAttributeValueToParts !==
|
||||
hass.formatEntityAttributeValueToParts ||
|
||||
value.formatEntityAttributeName !== hass.formatEntityAttributeName ||
|
||||
value.formatEntityName !== hass.formatEntityName
|
||||
) {
|
||||
return {
|
||||
formatEntityState: hass.formatEntityState,
|
||||
formatEntityStateToParts: hass.formatEntityStateToParts,
|
||||
formatEntityAttributeValue: hass.formatEntityAttributeValue,
|
||||
formatEntityAttributeValueToParts: hass.formatEntityAttributeValueToParts,
|
||||
formatEntityAttributeName: hass.formatEntityAttributeName,
|
||||
formatEntityName: hass.formatEntityName,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
export const updateHassGroups = {
|
||||
registries: updateRegistries,
|
||||
internationalization: updateInternationalization,
|
||||
@@ -190,5 +163,4 @@ export const updateHassGroups = {
|
||||
connection: updateConnection,
|
||||
ui: updateUi,
|
||||
config: updateConfig,
|
||||
formatters: updateFormatters,
|
||||
};
|
||||
|
||||
@@ -182,8 +182,6 @@ export interface GasSourceTypeEnergyPreference {
|
||||
entity_energy_price: string | null;
|
||||
number_energy_price: number | null;
|
||||
unit_of_measurement?: string | null;
|
||||
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface WaterSourceTypeEnergyPreference {
|
||||
@@ -202,8 +200,6 @@ export interface WaterSourceTypeEnergyPreference {
|
||||
entity_energy_price: string | null;
|
||||
number_energy_price: number | null;
|
||||
unit_of_measurement?: string | null;
|
||||
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export type EnergySource =
|
||||
|
||||
@@ -154,7 +154,7 @@ export const getRecorderInfo = (conn: Connection) =>
|
||||
});
|
||||
|
||||
export const getStatisticIds = (
|
||||
hass: Pick<HomeAssistant, "callWS">,
|
||||
hass: HomeAssistant,
|
||||
statistic_type?: "mean" | "sum"
|
||||
) =>
|
||||
hass.callWS<StatisticsMetaData[]>({
|
||||
@@ -227,7 +227,7 @@ export const fetchStatistic = (
|
||||
rolling_window: period.rolling_window,
|
||||
});
|
||||
|
||||
export const validateStatistics = (hass: Pick<HomeAssistant, "callWS">) =>
|
||||
export const validateStatistics = (hass: HomeAssistant) =>
|
||||
hass.callWS<StatisticsValidationResults>({
|
||||
type: "recorder/validate_statistics",
|
||||
});
|
||||
@@ -245,10 +245,7 @@ export const updateStatisticsMetadata = (
|
||||
unit_class,
|
||||
});
|
||||
|
||||
export const clearStatistics = (
|
||||
hass: Pick<HomeAssistant, "callWS">,
|
||||
statistic_ids: string[]
|
||||
) =>
|
||||
export const clearStatistics = (hass: HomeAssistant, statistic_ids: string[]) =>
|
||||
hass.callWS<undefined>({
|
||||
type: "recorder/clear_statistics",
|
||||
statistic_ids,
|
||||
@@ -372,5 +369,5 @@ export const getDisplayUnit = (
|
||||
export const isExternalStatistic = (statisticsId: string): boolean =>
|
||||
statisticsId.includes(":");
|
||||
|
||||
export const updateStatisticsIssues = (hass: Pick<HomeAssistant, "callWS">) =>
|
||||
export const updateStatisticsIssues = (hass: HomeAssistant) =>
|
||||
hass.callWS<undefined>({ type: "recorder/update_statistics_issues" });
|
||||
|
||||
@@ -2,11 +2,11 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-bottom-sheet";
|
||||
import "../../components/ha-dialog";
|
||||
import "../../components/ha-icon";
|
||||
import "../../components/ha-md-list";
|
||||
import "../../components/ha-md-list-item";
|
||||
import "../../components/ha-svg-icon";
|
||||
import "../../components/item/ha-list-item-button";
|
||||
import "../../components/list/ha-list-base";
|
||||
import "../../components/ha-dialog";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type { HassDialog } from "../make-dialog-manager";
|
||||
import type { ListItemsDialogParams } from "./show-list-items-dialog";
|
||||
@@ -51,30 +51,41 @@ export class ListItemsDialog
|
||||
|
||||
const content = html`
|
||||
<div class="container">
|
||||
<ha-list-base>
|
||||
<ha-md-list>
|
||||
${this._params.items.map(
|
||||
(item) => html`
|
||||
<ha-list-item-button @click=${this._itemClicked} .item=${item}>
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
@click=${this._itemClicked}
|
||||
.item=${item}
|
||||
>
|
||||
${item.iconPath
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
.path=${item.iconPath}
|
||||
slot="start"
|
||||
class="item-icon"
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: item.icon
|
||||
? html` <ha-icon icon=${item.icon} slot="start"></ha-icon> `
|
||||
? html`
|
||||
<ha-icon
|
||||
icon=${item.icon}
|
||||
slot="start"
|
||||
class="item-icon"
|
||||
></ha-icon>
|
||||
`
|
||||
: nothing}
|
||||
<span slot="headline">${item.label}</span>
|
||||
<span class="headline">${item.label}</span>
|
||||
${item.description
|
||||
? html`
|
||||
<span slot="supporting-text">${item.description}</span>
|
||||
<span class="supporting-text">${item.description}</span>
|
||||
`
|
||||
: nothing}
|
||||
</ha-list-item-button>
|
||||
</ha-md-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-list-base>
|
||||
</ha-md-list>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -102,16 +113,12 @@ export class ListItemsDialog
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-dialog,
|
||||
ha-bottom-sheet {
|
||||
ha-dialog {
|
||||
/* Place above other dialogs */
|
||||
--dialog-z-index: 104;
|
||||
--dialog-content-padding: 0;
|
||||
--ha-row-item-padding-inline: var(--ha-space-6);
|
||||
}
|
||||
|
||||
ha-bottom-sheet {
|
||||
--ha-bottom-sheet-content-padding: var(--ha-space-4) 0 0;
|
||||
--md-list-item-leading-space: 24px;
|
||||
--md-list-item-trailing-space: 24px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ class MoreInfoScript extends LitElement {
|
||||
...(this.data ? { data: this.data } : {}),
|
||||
...this._scriptData,
|
||||
}}
|
||||
.showAdvanced=${this.hass.userData?.showAdvanced}
|
||||
.narrow=${this.narrow}
|
||||
@value-changed=${this._scriptDataChanged}
|
||||
></ha-service-control>
|
||||
|
||||
@@ -9,9 +9,10 @@ import "../../../components/ha-alert";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-faded";
|
||||
import "../../../components/ha-markdown";
|
||||
import "../../../components/ha-md-list";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import "../../../components/ha-spinner";
|
||||
import "../../../components/ha-switch";
|
||||
import "../../../components/item/ha-row-item";
|
||||
import "../../../components/progress/ha-progress-bar";
|
||||
import type { BackupConfig } from "../../../data/backup";
|
||||
import { fetchBackupConfig } from "../../../data/backup";
|
||||
@@ -273,22 +274,24 @@ class MoreInfoUpdate extends LitElement {
|
||||
<div class="footer">
|
||||
${createBackupTexts
|
||||
? html`
|
||||
<ha-row-item>
|
||||
<span slot="headline">${createBackupTexts.title}</span>
|
||||
${createBackupTexts.description
|
||||
? html`
|
||||
<span slot="supporting-text">
|
||||
${createBackupTexts.description}
|
||||
</span>
|
||||
`
|
||||
: nothing}
|
||||
<ha-switch
|
||||
slot="end"
|
||||
.checked=${this._createBackup}
|
||||
@change=${this._createBackupChanged}
|
||||
.disabled=${updateIsInstalling(this.stateObj)}
|
||||
></ha-switch>
|
||||
</ha-row-item>
|
||||
<ha-md-list>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">${createBackupTexts.title}</span>
|
||||
${createBackupTexts.description
|
||||
? html`
|
||||
<span slot="supporting-text">
|
||||
${createBackupTexts.description}
|
||||
</span>
|
||||
`
|
||||
: nothing}
|
||||
<ha-switch
|
||||
slot="end"
|
||||
.checked=${this._createBackup}
|
||||
@change=${this._createBackupChanged}
|
||||
.disabled=${updateIsInstalling(this.stateObj)}
|
||||
></ha-switch>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
`
|
||||
: nothing}
|
||||
<div class="actions">
|
||||
@@ -481,9 +484,20 @@ class MoreInfoUpdate extends LitElement {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
ha-row-item {
|
||||
ha-md-list {
|
||||
width: 100%;
|
||||
--ha-row-item-padding-inline: var(--ha-space-6);
|
||||
box-sizing: border-box;
|
||||
margin-bottom: calc(var(--ha-space-4) * -1);
|
||||
margin-top: calc(var(--ha-space-1) * -1);
|
||||
--md-sys-color-surface: var(
|
||||
--ha-dialog-surface-background,
|
||||
var(--mdc-theme-surface, #fff)
|
||||
);
|
||||
}
|
||||
|
||||
ha-md-list-item {
|
||||
--md-list-item-leading-space: var(--ha-space-6);
|
||||
--md-list-item-trailing-space: var(--ha-space-6);
|
||||
}
|
||||
|
||||
.actions {
|
||||
|
||||
@@ -2,9 +2,8 @@ import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../components/ha-alert";
|
||||
import "../../components/ha-icon";
|
||||
import "../../components/ha-md-list-item";
|
||||
import "../../components/ha-spinner";
|
||||
import "../../components/item/ha-list-item-button";
|
||||
import "../../components/list/ha-list-base";
|
||||
import type {
|
||||
ExternalEntityAddToAction,
|
||||
ExternalEntityAddToActions,
|
||||
@@ -91,23 +90,24 @@ export class HaMoreInfoAddTo extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-list-base>
|
||||
${this._externalActions?.actions.map(
|
||||
<div class="actions-list">
|
||||
${this._externalActions.actions.map(
|
||||
(action) => html`
|
||||
<ha-list-item-button
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
.disabled=${!action.enabled}
|
||||
.action=${action}
|
||||
@click=${this._actionSelected}
|
||||
>
|
||||
<ha-icon slot="start" .icon=${action.mdi_icon}></ha-icon>
|
||||
<span slot="headline">${action.name}</span>
|
||||
<span>${action.name}</span>
|
||||
${action.details
|
||||
? html`<span slot="supporting-text">${action.details}</span>`
|
||||
: nothing}
|
||||
</ha-list-item-button>
|
||||
</ha-md-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-list-base>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -125,6 +125,11 @@ export class HaMoreInfoAddTo extends LitElement {
|
||||
padding: var(--ha-space-8);
|
||||
}
|
||||
|
||||
.actions-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
ha-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -57,6 +57,7 @@ class HaMoreInfoDetails extends LitElement {
|
||||
<div class="content">
|
||||
${this.yamlMode
|
||||
? html`<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.value=${yamlData}
|
||||
read-only
|
||||
auto-update
|
||||
|
||||
@@ -23,16 +23,12 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
|
||||
|
||||
@state() private _notifications: PersistentNotification[] = [];
|
||||
|
||||
@state() public _open = false;
|
||||
|
||||
@state() private _drawerOpen = false;
|
||||
@state() private _open = false;
|
||||
|
||||
@query("ha-drawer") private _drawer?: HaDrawer;
|
||||
|
||||
private _unsubNotifications?: UnsubscribeFunc;
|
||||
|
||||
private _openAnimationFrame?: number;
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
window.addEventListener("location-changed", this.closeDialog);
|
||||
@@ -41,10 +37,6 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
window.removeEventListener("location-changed", this.closeDialog);
|
||||
if (this._openAnimationFrame !== undefined) {
|
||||
cancelAnimationFrame(this._openAnimationFrame);
|
||||
this._openAnimationFrame = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
showDialog({ narrow }) {
|
||||
@@ -59,21 +51,22 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
|
||||
}
|
||||
);
|
||||
this.style.setProperty(
|
||||
"--ha-sidebar-width",
|
||||
"--mdc-drawer-width",
|
||||
`min(100vw, calc(${narrow ? window.innerWidth + "px" : "500px"} + var(--safe-area-inset-left, 0px)))`
|
||||
);
|
||||
this._open = true;
|
||||
}
|
||||
|
||||
closeDialog = () => {
|
||||
if (this._drawerOpen && this._drawer) {
|
||||
if (this._drawer) {
|
||||
this._drawer.open = false;
|
||||
this._drawerOpen = false;
|
||||
return;
|
||||
}
|
||||
this._drawerOpen = false;
|
||||
this._open = false;
|
||||
this._finalizeClose();
|
||||
if (this._unsubNotifications) {
|
||||
this._unsubNotifications();
|
||||
this._unsubNotifications = undefined;
|
||||
}
|
||||
this._notifications = [];
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
};
|
||||
|
||||
public willUpdate(changedProps: PropertyValues<this>): void {
|
||||
@@ -84,17 +77,6 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues<this>) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("_open") && this._open && !this._drawerOpen) {
|
||||
this._openAnimationFrame = requestAnimationFrame(() => {
|
||||
this._openAnimationFrame = undefined;
|
||||
this._drawerOpen = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._open) {
|
||||
return nothing;
|
||||
@@ -122,8 +104,8 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
|
||||
return html`
|
||||
<ha-drawer
|
||||
type="modal"
|
||||
.open=${this._drawerOpen}
|
||||
@hass-drawer-closed=${this._dialogClosed}
|
||||
open
|
||||
@MDCDrawer:closed=${this._dialogClosed}
|
||||
.direction=${computeRTLDirection(this.hass)}
|
||||
>
|
||||
<ha-header-bar>
|
||||
@@ -175,9 +157,7 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
|
||||
|
||||
private _dialogClosed(ev: Event) {
|
||||
ev.stopPropagation();
|
||||
this._drawerOpen = false;
|
||||
this._open = false;
|
||||
this._finalizeClose();
|
||||
}
|
||||
|
||||
private _dismissAll() {
|
||||
@@ -185,19 +165,6 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private _finalizeClose() {
|
||||
if (this._openAnimationFrame !== undefined) {
|
||||
cancelAnimationFrame(this._openAnimationFrame);
|
||||
this._openAnimationFrame = undefined;
|
||||
}
|
||||
if (this._unsubNotifications) {
|
||||
this._unsubNotifications();
|
||||
this._unsubNotifications = undefined;
|
||||
}
|
||||
this._notifications = [];
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected supportedSingleKeyShortcuts(): SupportedShortcuts {
|
||||
return {
|
||||
Escape: () => this.closeDialog(),
|
||||
|
||||
@@ -10,15 +10,14 @@ import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/animation/ha-fade-in";
|
||||
import "../../components/ha-adaptive-dialog";
|
||||
import "../../components/ha-alert";
|
||||
import "../../components/ha-expansion-panel";
|
||||
import "../../components/animation/ha-fade-in";
|
||||
import "../../components/ha-icon-next";
|
||||
import "../../components/ha-md-list";
|
||||
import "../../components/ha-md-list-item";
|
||||
import "../../components/ha-spinner";
|
||||
import "../../components/item/ha-list-item-button";
|
||||
import type { HaListItemButton } from "../../components/item/ha-list-item-button";
|
||||
import "../../components/list/ha-list-base";
|
||||
import "../../components/progress/ha-progress-bar";
|
||||
import { fetchBackupInfo } from "../../data/backup";
|
||||
import type { BackupManagerState } from "../../data/backup_manager";
|
||||
@@ -131,8 +130,9 @@ class DialogRestart extends LitElement {
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<ha-list-base dialogInitialFocus>
|
||||
<ha-list-item-button
|
||||
<ha-md-list dialogInitialFocus>
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
@click=${this._reload}
|
||||
.disabled=${this._loadingBackupInfo}
|
||||
>
|
||||
@@ -148,8 +148,9 @@ class DialogRestart extends LitElement {
|
||||
<ha-svg-icon .path=${mdiAutoFix}></ha-svg-icon>
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>
|
||||
<ha-list-item-button
|
||||
</ha-md-list-item>
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
.action=${"restart"}
|
||||
@click=${this._handleAction}
|
||||
.disabled=${this._loadingBackupInfo}
|
||||
@@ -166,17 +167,18 @@ class DialogRestart extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>
|
||||
</ha-list-base>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
<ha-expansion-panel
|
||||
.header=${this.hass.localize(
|
||||
"ui.dialogs.restart.advanced_options"
|
||||
)}
|
||||
>
|
||||
<ha-list-base>
|
||||
<ha-md-list>
|
||||
${showRebootShutdown
|
||||
? html`
|
||||
<ha-list-item-button
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
.action=${"reboot"}
|
||||
@click=${this._handleAction}
|
||||
.disabled=${this._loadingBackupInfo}
|
||||
@@ -195,8 +197,9 @@ class DialogRestart extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>
|
||||
<ha-list-item-button
|
||||
</ha-md-list-item>
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
.action=${"shutdown"}
|
||||
@click=${this._handleAction}
|
||||
.disabled=${this._loadingBackupInfo}
|
||||
@@ -215,10 +218,11 @@ class DialogRestart extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>
|
||||
</ha-md-list-item>
|
||||
`
|
||||
: nothing}
|
||||
<ha-list-item-button
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
.action=${"restart-safe-mode"}
|
||||
@click=${this._handleAction}
|
||||
.disabled=${this._loadingBackupInfo}
|
||||
@@ -240,8 +244,8 @@ class DialogRestart extends LitElement {
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>
|
||||
</ha-list-base>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
</ha-expansion-panel>
|
||||
`}
|
||||
</div>
|
||||
@@ -320,13 +324,16 @@ class DialogRestart extends LitElement {
|
||||
}
|
||||
};
|
||||
|
||||
private async _handleAction(ev: Event) {
|
||||
private async _handleAction(ev) {
|
||||
if (this._loadingBackupInfo) {
|
||||
return;
|
||||
}
|
||||
this._loadingBackupInfo = true;
|
||||
const action = (ev.currentTarget as HaListItemButton & { action: string })
|
||||
.action as "restart" | "reboot" | "shutdown" | "restart-safe-mode";
|
||||
const action = ev.currentTarget.action as
|
||||
| "restart"
|
||||
| "reboot"
|
||||
| "shutdown"
|
||||
| "restart-safe-mode";
|
||||
|
||||
const backupState = await this._loadBackupState();
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/item/ha-list-item-button";
|
||||
import type { HaListItemButton } from "../../components/item/ha-list-item-button";
|
||||
import "../../components/list/ha-list-base";
|
||||
import "../../components/ha-md-list";
|
||||
import "../../components/ha-md-list-item";
|
||||
import type { AssistSatelliteConfiguration } from "../../data/assist_satellite";
|
||||
import { setWakeWords } from "../../data/assist_satellite";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
@@ -36,28 +35,28 @@ export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement {
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<ha-list-base>
|
||||
<ha-md-list>
|
||||
${this.assistConfiguration!.available_wake_words.map(
|
||||
(wakeWord) =>
|
||||
html`<ha-list-item-button
|
||||
html`<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
@click=${this._wakeWordPicked}
|
||||
.value=${wakeWord.id}
|
||||
>
|
||||
${wakeWord.wake_word}
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>`
|
||||
</ha-md-list-item>`
|
||||
)}
|
||||
</ha-list-base>`;
|
||||
</ha-md-list>`;
|
||||
}
|
||||
|
||||
private async _wakeWordPicked(ev: Event) {
|
||||
private async _wakeWordPicked(ev) {
|
||||
if (!this.assistEntityId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wakeWordId = (
|
||||
ev.currentTarget as HaListItemButton & { value: string }
|
||||
).value;
|
||||
const wakeWordId = ev.currentTarget.value;
|
||||
|
||||
await setWakeWords(this.hass, this.assistEntityId, [wakeWordId]);
|
||||
this._nextStep();
|
||||
@@ -76,7 +75,7 @@ export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement {
|
||||
.padding {
|
||||
padding: 24px;
|
||||
}
|
||||
ha-list-base {
|
||||
ha-md-list {
|
||||
width: 100%;
|
||||
text-align: initial;
|
||||
margin-bottom: 24px;
|
||||
|
||||
@@ -363,6 +363,9 @@ export class HaVoiceAssistantSetupStepSuccess extends LitElement {
|
||||
static styles = [
|
||||
AssistantSetupStyles,
|
||||
css`
|
||||
ha-md-list-item {
|
||||
text-align: initial;
|
||||
}
|
||||
ha-tts-voice-picker {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -214,8 +214,10 @@ export class HaTabsSubpageDataTable extends KeyboardShortcutMixin(LitElement) {
|
||||
if (
|
||||
changedProperties.has("tabs") ||
|
||||
(changedProperties.has("hass") &&
|
||||
this.hass?.config.components !==
|
||||
changedProperties.get("hass")?.config.components)
|
||||
(this.hass?.config.components !==
|
||||
changedProperties.get("hass")?.config.components ||
|
||||
this.hass?.userData?.showAdvanced !==
|
||||
changedProperties.get("hass")?.userData?.showAdvanced))
|
||||
) {
|
||||
this.showTabs =
|
||||
this.tabs.filter((page) => canShowPage(this.hass, page)).length > 1;
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface PageNavigation {
|
||||
name?: string;
|
||||
not_component?: string | string[];
|
||||
core?: boolean;
|
||||
advancedOnly?: boolean;
|
||||
/** Hide from non-admin users in filtered navigation and quick bar. */
|
||||
adminOnly?: boolean;
|
||||
iconPath?: string;
|
||||
|
||||
@@ -56,7 +56,7 @@ export class HomeAssistantMain extends LitElement {
|
||||
.type=${sidebarNarrow ? "modal" : ""}
|
||||
.open=${sidebarNarrow ? this._drawerOpen : false}
|
||||
.direction=${computeRTLDirection(this.hass)}
|
||||
@hass-drawer-closed=${this._drawerClosed}
|
||||
@MDCDrawer:closed=${this._drawerClosed}
|
||||
>
|
||||
<ha-sidebar
|
||||
.hass=${this.hass}
|
||||
@@ -152,16 +152,16 @@ export class HomeAssistantMain extends LitElement {
|
||||
color: var(--primary-text-color);
|
||||
/* remove the grey tap highlights in iOS on the fullscreen touch targets */
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
--ha-sidebar-width: calc(56px + var(--safe-area-inset-left, 0px));
|
||||
--mdc-top-app-bar-width: calc(100% - var(--ha-sidebar-width));
|
||||
--mdc-drawer-width: calc(56px + var(--safe-area-inset-left, 0px));
|
||||
--mdc-top-app-bar-width: calc(100% - var(--mdc-drawer-width));
|
||||
--safe-area-content-inset-left: 0px;
|
||||
--safe-area-content-inset-right: var(--safe-area-inset-right);
|
||||
}
|
||||
:host([expanded]) {
|
||||
--ha-sidebar-width: calc(256px + var(--safe-area-inset-left, 0px));
|
||||
--mdc-drawer-width: calc(256px + var(--safe-area-inset-left, 0px));
|
||||
}
|
||||
:host([modal]) {
|
||||
--ha-sidebar-width: unset;
|
||||
--mdc-drawer-width: unset;
|
||||
--mdc-top-app-bar-width: unset;
|
||||
--safe-area-content-inset-left: var(--safe-area-inset-left);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { LOCAL_TIME_ZONE } from "../common/datetime/resolve-time-zone";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
@@ -42,8 +42,6 @@ class OnboardingCoreConfig extends LitElement {
|
||||
|
||||
@state() private _skipCore = false;
|
||||
|
||||
@query("ha-country-picker") private _countryPicker?: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._location) {
|
||||
return html`<onboarding-location
|
||||
@@ -145,7 +143,10 @@ class OnboardingCoreConfig extends LitElement {
|
||||
|
||||
fireEvent(this, "onboarding-progress", { increase: 0.5 });
|
||||
await this.updateComplete;
|
||||
setTimeout(() => this._countryPicker!.focus(), 100);
|
||||
setTimeout(
|
||||
() => this.renderRoot.querySelector("ha-country-picker")!.focus(),
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
private async _save(ev) {
|
||||
|
||||
@@ -64,8 +64,6 @@ class OnboardingLocation extends LitElement {
|
||||
|
||||
@query("ha-locations-editor", true) private map!: HaLocationsEditor;
|
||||
|
||||
@query("ha-input") private _input?: HTMLElement;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const addressAttribution = this.onboardingLocalize(
|
||||
"ui.panel.page-onboarding.core-config.location_address",
|
||||
@@ -203,7 +201,7 @@ class OnboardingLocation extends LitElement {
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues<this>) {
|
||||
super.firstUpdated(changedProps);
|
||||
setTimeout(() => this._input!.focus(), 100);
|
||||
setTimeout(() => this.renderRoot.querySelector("ha-input")!.focus(), 100);
|
||||
this.addEventListener("keyup", (ev) => {
|
||||
if (ev.key === "Enter") {
|
||||
this._save(ev);
|
||||
|
||||
@@ -5,9 +5,9 @@ import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../common/translations/localize";
|
||||
import "../components/ha-button";
|
||||
import "../components/ha-icon-next";
|
||||
import "../components/item/ha-list-item-button";
|
||||
import "../components/list/ha-list-base";
|
||||
import "../components/ha-icon-button-next";
|
||||
import "../components/ha-md-list";
|
||||
import "../components/ha-md-list-item";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { onBoardingStyles } from "./styles";
|
||||
|
||||
@@ -37,8 +37,8 @@ class OnboardingWelcome extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ha-list-base>
|
||||
<ha-list-item-button @click=${this._restoreBackupUpload}>
|
||||
<ha-md-list>
|
||||
<ha-md-list-item type="button" @click=${this._restoreBackupUpload}>
|
||||
<div slot="headline">
|
||||
${this.localize("ui.panel.page-onboarding.restore.upload_backup")}
|
||||
</div>
|
||||
@@ -47,18 +47,18 @@ class OnboardingWelcome extends LitElement {
|
||||
"ui.panel.page-onboarding.restore.options.upload_description"
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>
|
||||
<ha-list-item-button @click=${this._restoreBackupCloud}>
|
||||
<ha-icon-button-next slot="end"></ha-icon-button-next>
|
||||
</ha-md-list-item>
|
||||
<ha-md-list-item type="button" @click=${this._restoreBackupCloud}>
|
||||
<div slot="headline">Home Assistant Cloud</div>
|
||||
<div slot="supporting-text">
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.ha-cloud.description"
|
||||
)}
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>
|
||||
</ha-list-base>
|
||||
<ha-icon-button-next slot="end"></ha-icon-button-next>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -123,10 +123,11 @@ class OnboardingWelcome extends LitElement {
|
||||
padding: 0 var(--ha-space-4);
|
||||
}
|
||||
|
||||
ha-list-base {
|
||||
ha-md-list {
|
||||
width: 100%;
|
||||
padding-bottom: 0;
|
||||
--ha-row-item-padding-inline: 0;
|
||||
--md-list-item-leading-space: 0;
|
||||
--md-list-item-trailing-space: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -8,8 +8,9 @@ import type { HaProgressButton } from "../../components/buttons/ha-progress-butt
|
||||
import "../../components/ha-alert";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-icon-button-arrow-prev";
|
||||
import "../../components/ha-md-list";
|
||||
import "../../components/ha-md-list-item";
|
||||
import "../../components/input/ha-input";
|
||||
import "../../components/item/ha-row-item";
|
||||
import {
|
||||
getPreferredAgentForDownload,
|
||||
type BackupContentExtended,
|
||||
@@ -91,30 +92,33 @@ class OnboardingRestoreBackupRestore extends LitElement {
|
||||
</ha-alert>`
|
||||
: nothing}
|
||||
|
||||
<ha-row-item>
|
||||
<span slot="headline">
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.details.summary.created"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">${formattedDate}</span>
|
||||
</ha-row-item>
|
||||
${onlyHomeAssistantBackup
|
||||
? html`<ha-row-item>
|
||||
<span slot="headline">
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.details.summary.content"
|
||||
)}
|
||||
</span>
|
||||
<ha-backup-formfield-label
|
||||
slot="supporting-text"
|
||||
.version=${this.backup.homeassistant_version}
|
||||
.label=${this.localize(
|
||||
`ui.panel.page-onboarding.restore.data_picker.${this.backup.database_included ? "settings_and_history" : "settings"}`
|
||||
)}
|
||||
></ha-backup-formfield-label>
|
||||
</ha-row-item>`
|
||||
: nothing}
|
||||
<ha-md-list>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.details.summary.created"
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">${formattedDate}</span>
|
||||
</ha-md-list-item>
|
||||
${onlyHomeAssistantBackup
|
||||
? html`<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${this.localize(
|
||||
"ui.panel.page-onboarding.restore.details.summary.content"
|
||||
)}
|
||||
</span>
|
||||
<ha-backup-formfield-label
|
||||
slot="supporting-text"
|
||||
.version=${this.backup.homeassistant_version}
|
||||
.label=${this.localize(
|
||||
`ui.panel.page-onboarding.restore.data_picker.${this.backup.database_included ? "settings_and_history" : "settings"}`
|
||||
)}
|
||||
></ha-backup-formfield-label>
|
||||
</ha-md-list-item>`
|
||||
: nothing}
|
||||
</ha-md-list>
|
||||
|
||||
${!onlyHomeAssistantBackup
|
||||
? html`<h2>
|
||||
${this.localize("ui.panel.page-onboarding.restore.select_type")}
|
||||
@@ -308,8 +312,26 @@ class OnboardingRestoreBackupRestore extends LitElement {
|
||||
display: block;
|
||||
margin-top: 16px;
|
||||
}
|
||||
ha-row-item {
|
||||
--ha-row-item-padding-inline: 0;
|
||||
ha-md-list {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
ha-md-list-item {
|
||||
--md-list-item-leading-space: 0;
|
||||
--md-list-item-trailing-space: 0;
|
||||
--md-list-item-two-line-container-height: 64px;
|
||||
--md-list-item-supporting-text-size: 1rem;
|
||||
--md-list-item-label-text-size: 0.875rem;
|
||||
|
||||
--md-list-item-label-text-color: var(--secondary-text-color);
|
||||
--md-list-item-supporting-text-color: var(--primary-text-color);
|
||||
}
|
||||
ha-md-list-item [slot="supporting-text"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: var(--ha-space-2);
|
||||
line-height: var(--ha-line-height-condensed);
|
||||
}
|
||||
h2 {
|
||||
font-size: var(--ha-font-size-xl);
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoize from "memoize-one";
|
||||
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
|
||||
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
|
||||
@@ -103,8 +103,6 @@ export class HAFullCalendar extends LitElement {
|
||||
|
||||
@state() private _activeView = this.initialView;
|
||||
|
||||
@query("style[data-fullcalendar]") private _fullCalendarStyle?: HTMLElement;
|
||||
|
||||
// @ts-ignore
|
||||
private _resizeController = new ResizeController(this, {
|
||||
callback: () => this.calendar?.updateSize(),
|
||||
@@ -115,7 +113,7 @@ export class HAFullCalendar extends LitElement {
|
||||
super.disconnectedCallback();
|
||||
this.calendar?.destroy();
|
||||
this.calendar = undefined;
|
||||
this._fullCalendarStyle?.remove();
|
||||
this.renderRoot.querySelector("style[data-fullcalendar]")?.remove();
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
|
||||
+29
-17
@@ -13,12 +13,13 @@ import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/ha-alert";
|
||||
import "../../../../../components/ha-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-spinner";
|
||||
import "../../../../../components/ha-faded";
|
||||
import "../../../../../components/ha-markdown";
|
||||
import "../../../../../components/ha-spinner";
|
||||
import "../../../../../components/ha-md-list";
|
||||
import "../../../../../components/ha-md-list-item";
|
||||
import "../../../../../components/ha-switch";
|
||||
import type { HaSwitch } from "../../../../../components/ha-switch";
|
||||
import "../../../../../components/item/ha-row-item";
|
||||
import type { HassioAddonDetails } from "../../../../../data/hassio/addon";
|
||||
import {
|
||||
fetchHassioAddonChangelog,
|
||||
@@ -107,20 +108,25 @@ class SupervisorAppUpdateAvailableCard extends LitElement {
|
||||
${createBackupTexts
|
||||
? html`
|
||||
<hr />
|
||||
<ha-row-item>
|
||||
<span slot="headline">
|
||||
${createBackupTexts.title}
|
||||
</span>
|
||||
<ha-md-list>
|
||||
<ha-md-list-item>
|
||||
<span slot="headline">
|
||||
${createBackupTexts.title}
|
||||
</span>
|
||||
|
||||
${createBackupTexts.description
|
||||
? html`
|
||||
<span slot="supporting-text">
|
||||
${createBackupTexts.description}
|
||||
</span>
|
||||
`
|
||||
: nothing}
|
||||
<ha-switch slot="end" id="create-backup"></ha-switch>
|
||||
</ha-row-item>
|
||||
${createBackupTexts.description
|
||||
? html`
|
||||
<span slot="supporting-text">
|
||||
${createBackupTexts.description}
|
||||
</span>
|
||||
`
|
||||
: nothing}
|
||||
<ha-switch
|
||||
slot="end"
|
||||
id="create-backup"
|
||||
></ha-switch>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>
|
||||
`
|
||||
: nothing}
|
||||
`
|
||||
@@ -267,10 +273,16 @@ class SupervisorAppUpdateAvailableCard extends LitElement {
|
||||
margin: var(--ha-space-4) 0 0 0;
|
||||
}
|
||||
|
||||
ha-row-item {
|
||||
--ha-row-item-padding-inline: 0;
|
||||
ha-md-list {
|
||||
padding: 0;
|
||||
margin-bottom: calc(-1 * var(--ha-space-4));
|
||||
}
|
||||
|
||||
ha-md-list-item {
|
||||
--md-list-item-leading-space: 0;
|
||||
--md-list-item-trailing-space: 0;
|
||||
--md-item-overflow: visible;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "@home-assistant/webawesome/dist/components/tag/tag";
|
||||
import {
|
||||
mdiCheckCircle,
|
||||
mdiChip,
|
||||
mdiCircleOffOutline,
|
||||
mdiCursorDefaultClickOutline,
|
||||
mdiDocker,
|
||||
mdiExclamationThick,
|
||||
@@ -17,10 +17,11 @@ import {
|
||||
mdiNumeric6,
|
||||
mdiNumeric7,
|
||||
mdiNumeric8,
|
||||
mdiPlayCircle,
|
||||
mdiPound,
|
||||
mdiShield,
|
||||
} from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||
import type { CSSResultGroup, TemplateResult, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
@@ -30,7 +31,6 @@ import { atLeastVersion } from "../../../../../common/config/version";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { navigate } from "../../../../../common/navigate";
|
||||
import { capitalizeFirstLetter } from "../../../../../common/string/capitalize-first-letter";
|
||||
import type { LocalizeKeys } from "../../../../../common/translations/localize";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/chips/ha-assist-chip";
|
||||
import "../../../../../components/chips/ha-chip-set";
|
||||
@@ -79,9 +79,9 @@ import { bytesToString } from "../../../../../util/bytes-to-string";
|
||||
import { getAppDisplayName } from "../../common/app";
|
||||
import "../../components/supervisor-apps-card-content";
|
||||
import "../components/supervisor-app-metric";
|
||||
import "../components/supervisor-app-update-available-card";
|
||||
import { extractChangelog } from "../util/supervisor-app";
|
||||
import "./supervisor-app-system-managed";
|
||||
import "../components/supervisor-app-update-available-card";
|
||||
|
||||
const STAGE_ICON = {
|
||||
stable: mdiCheckCircle,
|
||||
@@ -203,10 +203,28 @@ class SupervisorAppInfo extends LitElement {
|
||||
: nothing}
|
||||
<div class="addon-version light-color">
|
||||
${this.addon.version
|
||||
? html`<supervisor-apps-state
|
||||
.state=${this.addon.state}
|
||||
></supervisor-apps-state>`
|
||||
: this.addon.version_latest}
|
||||
? html`
|
||||
${this._computeIsRunning
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.apps.dashboard.app_running"
|
||||
)}
|
||||
class="running"
|
||||
.path=${mdiPlayCircle}
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: html`
|
||||
<ha-svg-icon
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.apps.dashboard.app_stopped"
|
||||
)}
|
||||
class="stopped"
|
||||
.path=${mdiCircleOffOutline}
|
||||
></ha-svg-icon>
|
||||
`}
|
||||
`
|
||||
: html` ${this.addon.version_latest} `}
|
||||
</div>
|
||||
</div>
|
||||
<div class="description light-color">
|
||||
@@ -819,7 +837,7 @@ class SupervisorAppInfo extends LitElement {
|
||||
const id = ev.currentTarget.id as AddonCapability;
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
`ui.panel.config.apps.dashboard.capability.${id}.title` as LocalizeKeys
|
||||
`ui.panel.config.apps.dashboard.capability.${id}.title`
|
||||
),
|
||||
text: this.hass.localize(
|
||||
`ui.panel.config.apps.dashboard.capability.${id}.description`
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import "@home-assistant/webawesome/dist/components/tag/tag";
|
||||
import { mdiHelpCircleOutline } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type { AddonStage, AddonState } from "../../../../data/hassio/addon";
|
||||
import type { AddonStage } from "../../../../data/hassio/addon";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { getAppDisplayName } from "../common/app";
|
||||
import "./supervisor-apps-state";
|
||||
import "./supervisor-apps-tag";
|
||||
|
||||
export interface AppTag {
|
||||
label: string;
|
||||
variant: "brand" | "success" | "warning" | "danger" | "neutral";
|
||||
iconPath?: string;
|
||||
}
|
||||
|
||||
@customElement("supervisor-apps-card-content")
|
||||
class SupervisorAppsCardContent extends LitElement {
|
||||
@@ -25,13 +16,13 @@ class SupervisorAppsCardContent extends LitElement {
|
||||
|
||||
@property() public stage: AddonStage = "stable";
|
||||
|
||||
@property() public state: AddonState = null;
|
||||
|
||||
@property() public description?: string;
|
||||
|
||||
@property({ type: Boolean }) public available = true;
|
||||
|
||||
@property({ attribute: false }) public tags?: AppTag[];
|
||||
@property({ attribute: false }) public showTopbar = false;
|
||||
|
||||
@property({ attribute: false }) public topbarClass?: string;
|
||||
|
||||
@property({ attribute: false }) public iconTitle?: string;
|
||||
|
||||
@@ -42,87 +33,78 @@ class SupervisorAppsCardContent extends LitElement {
|
||||
@property({ attribute: false }) public iconImage?: string;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div class="app">
|
||||
<div class="icon-wrapper">
|
||||
${this.iconImage
|
||||
? html`
|
||||
<img
|
||||
class="icon-image"
|
||||
src=${this.iconImage}
|
||||
.title=${this.iconTitle}
|
||||
alt=${this.iconTitle ?? ""}
|
||||
/>
|
||||
`
|
||||
: html`
|
||||
<ha-svg-icon
|
||||
class="app-icon"
|
||||
.path=${this.icon}
|
||||
.title=${this.iconTitle}
|
||||
></ha-svg-icon>
|
||||
`}
|
||||
</div>
|
||||
<div>
|
||||
<div class="title-row">
|
||||
<div class="title">
|
||||
${getAppDisplayName(this.title, this.stage)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="addition">
|
||||
${this.description}
|
||||
${
|
||||
/* treat as available when undefined */
|
||||
this.available === false ? " (Not available)" : ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${this.tags?.length || this.state
|
||||
? html`
|
||||
<div class="footer">
|
||||
<supervisor-apps-state
|
||||
.state=${this.state || "unknown"}
|
||||
></supervisor-apps-state>
|
||||
const stageLabel =
|
||||
this.stage !== "stable"
|
||||
? this.hass.localize(
|
||||
`ui.panel.config.apps.dashboard.capability.stages.${this.stage}`
|
||||
)
|
||||
: undefined;
|
||||
|
||||
${this.tags?.length
|
||||
? html`<div class="tags">
|
||||
${this.tags.map(
|
||||
(tag) =>
|
||||
html`<supervisor-apps-tag
|
||||
.variant=${tag.variant}
|
||||
.iconPath=${tag.iconPath}
|
||||
.label=${tag.label}
|
||||
></supervisor-apps-tag>`
|
||||
)}
|
||||
</div>`
|
||||
: nothing}
|
||||
return html`
|
||||
${this.showTopbar
|
||||
? html` <div class="topbar ${this.topbarClass}"></div> `
|
||||
: ""}
|
||||
${this.iconImage
|
||||
? html`
|
||||
<div class="icon_image ${this.iconClass}">
|
||||
<img
|
||||
src=${this.iconImage}
|
||||
.title=${this.iconTitle}
|
||||
alt=${this.iconTitle ?? ""}
|
||||
/>
|
||||
<div></div>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
: html`
|
||||
<ha-svg-icon
|
||||
class=${this.iconClass!}
|
||||
.path=${this.icon}
|
||||
.title=${this.iconTitle}
|
||||
></ha-svg-icon>
|
||||
`}
|
||||
<div>
|
||||
<div class="title-row">
|
||||
<div class="title">${getAppDisplayName(this.title, this.stage)}</div>
|
||||
${stageLabel
|
||||
? html` <span class="stage ${this.stage}"> ${stageLabel} </span> `
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="addition">
|
||||
${this.description}
|
||||
${
|
||||
/* treat as available when undefined */
|
||||
this.available === false ? " (Not available)" : ""
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.app {
|
||||
margin-bottom: var(--ha-space-2);
|
||||
gap: var(--ha-space-4);
|
||||
display: flex;
|
||||
:host {
|
||||
direction: ltr;
|
||||
}
|
||||
.icon-wrapper {
|
||||
position: relative;
|
||||
margin-top: var(--ha-space-1);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.app-icon {
|
||||
|
||||
ha-svg-icon {
|
||||
margin-right: var(--ha-space-6);
|
||||
margin-left: var(--ha-space-2);
|
||||
margin-top: var(--ha-space-2);
|
||||
margin-top: var(--ha-space-3);
|
||||
float: left;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.icon-image {
|
||||
max-height: 40px;
|
||||
max-width: 40px;
|
||||
ha-svg-icon.update {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
ha-svg-icon.running,
|
||||
ha-svg-icon.installed {
|
||||
color: var(--success-color);
|
||||
}
|
||||
ha-svg-icon.hassupdate,
|
||||
ha-svg-icon.backup {
|
||||
color: var(--state-icon-color);
|
||||
}
|
||||
ha-svg-icon.not_available {
|
||||
color: var(--error-color);
|
||||
}
|
||||
.title {
|
||||
flex: 1;
|
||||
@@ -138,6 +120,22 @@ class SupervisorAppsCardContent extends LitElement {
|
||||
gap: var(--ha-space-2);
|
||||
min-width: 0;
|
||||
}
|
||||
.stage {
|
||||
flex: none;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
padding: 4px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.stage.experimental {
|
||||
color: var(--warning-color);
|
||||
background-color: rgba(var(--rgb-warning-color), 0.12);
|
||||
}
|
||||
.stage.deprecated {
|
||||
color: var(--error-color);
|
||||
background-color: rgba(var(--rgb-error-color), 0.12);
|
||||
}
|
||||
.addition {
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: var(--ha-space-1);
|
||||
@@ -146,18 +144,43 @@ class SupervisorAppsCardContent extends LitElement {
|
||||
height: 2.4em;
|
||||
line-height: var(--ha-line-height-condensed);
|
||||
}
|
||||
.footer {
|
||||
border-top: var(--ha-border-width-sm) solid
|
||||
var(--ha-color-border-neutral-quiet);
|
||||
padding-top: var(--ha-space-2);
|
||||
display: flex;
|
||||
gap: var(--ha-space-2);
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
.icon_image img {
|
||||
max-height: 40px;
|
||||
max-width: 40px;
|
||||
margin-top: var(--ha-space-1);
|
||||
margin-right: var(--ha-space-4);
|
||||
float: left;
|
||||
}
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: var(--ha-space-2);
|
||||
.icon_image.stopped,
|
||||
.icon_image.not_available {
|
||||
filter: grayscale(1);
|
||||
}
|
||||
.dot {
|
||||
position: absolute;
|
||||
background-color: var(--warning-color);
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: var(--ha-space-2);
|
||||
right: var(--ha-space-2);
|
||||
border-radius: var(--ha-border-radius-circle);
|
||||
}
|
||||
.topbar {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-top-left-radius: 2px;
|
||||
border-top-right-radius: 2px;
|
||||
}
|
||||
.topbar.installed {
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
.topbar.update {
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
.topbar.unavailable {
|
||||
background-color: var(--error-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import { internationalizationContext } from "../../../../data/context";
|
||||
import type { AddonState } from "../../../../data/hassio/addon";
|
||||
|
||||
@customElement("supervisor-apps-state")
|
||||
class SupervisorAppsState extends LitElement {
|
||||
@property() public state: Exclude<AddonState, null> = "unknown";
|
||||
|
||||
@state()
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
${this.state === "unknown"
|
||||
? html`<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>`
|
||||
: html` <div class="dot state-${this.state}"></div> `}
|
||||
<span
|
||||
>${this._i18n.localize(
|
||||
`ui.panel.config.apps.dashboard.capability.state.${this.state}`
|
||||
)}</span
|
||||
>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-2);
|
||||
color: var(--ha-color-text-secondary);
|
||||
font-size: var(--ha-font-size-m);
|
||||
}
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: var(--ha-border-radius-circle);
|
||||
background-color: var(--ha-color-on-neutral-normal);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.dot.state-started {
|
||||
background-color: var(--ha-color-green-80);
|
||||
animation: state-dot-pulse 1.8s infinite;
|
||||
}
|
||||
.dot.state-startup {
|
||||
background-color: var(--ha-color-on-warning-normal);
|
||||
}
|
||||
.dot.state-error {
|
||||
background-color: var(--ha-color-on-danger-normal);
|
||||
}
|
||||
ha-svg-icon {
|
||||
--mdc-icon-size: 20px;
|
||||
}
|
||||
@keyframes state-dot-pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(var(--rgb-success-color), 0.6);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 6px rgba(var(--rgb-success-color), 0);
|
||||
}
|
||||
}
|
||||
@media (prefers-reduced-motion) {
|
||||
.dot.state-started {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"supervisor-apps-state": SupervisorAppsState;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import "@home-assistant/webawesome/dist/components/tag/tag";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
|
||||
@customElement("supervisor-apps-tag")
|
||||
class SupervisorAppsTag extends LitElement {
|
||||
@property() public variant:
|
||||
| "brand"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger"
|
||||
| "neutral" = "neutral";
|
||||
|
||||
@property({ attribute: "icon-path" }) public iconPath?: string;
|
||||
|
||||
@property() public label!: string;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<wa-tag .variant=${this.variant}>
|
||||
${this.iconPath
|
||||
? html`<ha-svg-icon .path=${this.iconPath}></ha-svg-icon>`
|
||||
: nothing}
|
||||
${this.label}
|
||||
</wa-tag>`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
wa-tag {
|
||||
font-size: var(--ha-font-size-xs);
|
||||
border-radius: var(--ha-border-radius-pill);
|
||||
height: 20px;
|
||||
border: none;
|
||||
padding-inline: var(--ha-space-1) var(--ha-space-2);
|
||||
}
|
||||
wa-tag ha-svg-icon {
|
||||
--mdc-icon-size: 16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
wa-tag[variant="success"] {
|
||||
color: var(--ha-color-on-success-normal);
|
||||
}
|
||||
wa-tag[variant="warning"] {
|
||||
color: var(--ha-color-on-warning-normal);
|
||||
}
|
||||
wa-tag[variant="danger"] {
|
||||
color: var(--ha-color-on-error-normal);
|
||||
}
|
||||
wa-tag[variant="neutral"] {
|
||||
color: var(--ha-color-on-neutral-normal);
|
||||
}
|
||||
wa-tag[variant="brand"] {
|
||||
color: var(--ha-color-on-primary-normal);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"supervisor-apps-tag": SupervisorAppsTag;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
import {
|
||||
mdiAlertDecagramOutline,
|
||||
mdiArrowUpBoldCircle,
|
||||
mdiArrowUpBoldCircleOutline,
|
||||
mdiFlask,
|
||||
mdiPuzzle,
|
||||
mdiRefresh,
|
||||
mdiStorePlus,
|
||||
@@ -32,9 +29,7 @@ import "../../../layouts/hass-error-screen";
|
||||
import "../../../layouts/hass-loading-screen";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
import { getAppDisplayName } from "./common/app";
|
||||
import "./components/supervisor-apps-card-content";
|
||||
import type { AppTag } from "./components/supervisor-apps-card-content";
|
||||
import { supervisorAppsStyle } from "./resources/supervisor-apps-style";
|
||||
|
||||
@customElement("ha-config-apps-installed")
|
||||
@@ -101,59 +96,65 @@ export class HaConfigAppsInstalled extends LitElement {
|
||||
</ha-input-search>
|
||||
</div>
|
||||
<div class="content">
|
||||
${addons.length === 0
|
||||
? html`
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<button class="link" @click=${this._openStore}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.apps.installed.no_apps"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
: addons.map(
|
||||
(addon) => html`
|
||||
<ha-card
|
||||
role="button"
|
||||
tabindex="0"
|
||||
outlined
|
||||
.addon=${addon}
|
||||
@click=${this._addonTapped}
|
||||
aria-label=${getAppDisplayName(addon.name, addon.stage)}
|
||||
>
|
||||
<div class="card-group">
|
||||
${addons.length === 0
|
||||
? html`
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<supervisor-apps-card-content
|
||||
.hass=${this.hass}
|
||||
.title=${addon.name}
|
||||
.stage=${addon.stage}
|
||||
.description=${addon.description}
|
||||
available
|
||||
.tags=${this._getAppTags(addon)}
|
||||
.state=${addon.state}
|
||||
.icon=${addon.update_available
|
||||
? mdiArrowUpBoldCircle
|
||||
: mdiPuzzle}
|
||||
.iconTitle=${addon.state !== "started"
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.apps.installed.app_stopped"
|
||||
)
|
||||
: addon.update_available
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.apps.installed.app_update_available"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.apps.installed.app_running"
|
||||
)}
|
||||
.iconImage=${addon.icon
|
||||
? `/api/hassio/addons/${addon.slug}/icon`
|
||||
: undefined}
|
||||
></supervisor-apps-card-content>
|
||||
<button class="link" @click=${this._openStore}>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.apps.installed.no_apps"
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
: addons.map(
|
||||
(addon) => html`
|
||||
<ha-card
|
||||
outlined
|
||||
.addon=${addon}
|
||||
@click=${this._addonTapped}
|
||||
>
|
||||
<div class="card-content">
|
||||
<supervisor-apps-card-content
|
||||
.hass=${this.hass}
|
||||
.title=${addon.name}
|
||||
.stage=${addon.stage}
|
||||
.description=${addon.description}
|
||||
available
|
||||
.showTopbar=${addon.update_available}
|
||||
topbarClass="update"
|
||||
.icon=${addon.update_available
|
||||
? mdiArrowUpBoldCircle
|
||||
: mdiPuzzle}
|
||||
.iconTitle=${addon.state !== "started"
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.apps.installed.app_stopped"
|
||||
)
|
||||
: addon.update_available
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.apps.installed.app_update_available"
|
||||
)
|
||||
: this.hass.localize(
|
||||
"ui.panel.config.apps.installed.app_running"
|
||||
)}
|
||||
.iconClass=${addon.update_available
|
||||
? addon.state === "started"
|
||||
? "update"
|
||||
: "update stopped"
|
||||
: addon.state === "started"
|
||||
? "running"
|
||||
: "stopped"}
|
||||
.iconImage=${addon.icon
|
||||
? `/api/hassio/addons/${addon.slug}/icon`
|
||||
: undefined}
|
||||
></supervisor-apps-card-content>
|
||||
</div>
|
||||
</ha-card>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ha-button size="large" href="/config/apps/available">
|
||||
@@ -216,32 +217,6 @@ export class HaConfigAppsInstalled extends LitElement {
|
||||
navigate("/config/apps/available");
|
||||
}
|
||||
|
||||
private _getAppTags(addon: HassioAddonInfo): AppTag[] {
|
||||
const labels: AppTag[] = [];
|
||||
|
||||
if (addon.update_available) {
|
||||
labels.push({
|
||||
label: this.hass.localize(
|
||||
`ui.panel.config.apps.state.update_available`
|
||||
),
|
||||
variant: "brand",
|
||||
iconPath: mdiArrowUpBoldCircleOutline,
|
||||
});
|
||||
}
|
||||
if (addon.stage !== "stable") {
|
||||
labels.push({
|
||||
label: this.hass.localize(
|
||||
`ui.panel.config.apps.dashboard.capability.stages.${addon.stage}`
|
||||
),
|
||||
variant: addon.stage === "experimental" ? "warning" : "danger",
|
||||
iconPath:
|
||||
addon.stage === "experimental" ? mdiFlask : mdiAlertDecagramOutline,
|
||||
});
|
||||
}
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
static styles: CSSResultGroup = [
|
||||
supervisorAppsStyle,
|
||||
css`
|
||||
@@ -254,10 +229,7 @@ export class HaConfigAppsInstalled extends LitElement {
|
||||
ha-card {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
ha-card:hover {
|
||||
background-color: var(--ha-color-fill-neutral-quiet-resting);
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.search {
|
||||
@@ -275,13 +247,10 @@ export class HaConfigAppsInstalled extends LitElement {
|
||||
.content {
|
||||
padding: var(--ha-space-4);
|
||||
margin-bottom: var(--ha-space-18);
|
||||
gap: var(--ha-space-4);
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(336px, 100%), 1fr));
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: var(--ha-space-4) var(--ha-space-4) var(--ha-space-2);
|
||||
padding: var(--ha-space-4);
|
||||
}
|
||||
|
||||
button.link {
|
||||
|
||||
@@ -71,6 +71,7 @@ export default class HaAutomationActionEditor extends LitElement {
|
||||
`
|
||||
: nothing}
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this.action}
|
||||
@value-changed=${this._onYamlChange}
|
||||
.readOnly=${this.disabled}
|
||||
|
||||
@@ -54,6 +54,7 @@ export class HaEventAction extends LitElement implements ActionElement {
|
||||
@change=${this._eventChanged}
|
||||
></ha-input>
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.event.event_data"
|
||||
)}
|
||||
|
||||
@@ -85,6 +85,7 @@ export class HaServiceAction extends LitElement implements ActionElement {
|
||||
.hass=${this.hass}
|
||||
.value=${this._action}
|
||||
.disabled=${this.disabled}
|
||||
.showAdvanced=${this.hass.userData?.showAdvanced}
|
||||
.hidePicker=${!!this._action.metadata}
|
||||
@value-changed=${this._actionChanged}
|
||||
></ha-service-control>
|
||||
|
||||
@@ -47,6 +47,8 @@ import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-icon-button-prev";
|
||||
import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-md-list";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import type { PickerComboBoxItem } from "../../../components/ha-picker-combo-box";
|
||||
import "../../../components/ha-section-title";
|
||||
import "../../../components/ha-service-icon";
|
||||
@@ -54,8 +56,6 @@ import "../../../components/ha-tooltip";
|
||||
import { TRIGGER_ICONS } from "../../../components/ha-trigger-icon";
|
||||
import "../../../components/input/ha-input-search";
|
||||
import type { HaInputSearch } from "../../../components/input/ha-input-search";
|
||||
import "../../../components/item/ha-list-item-button";
|
||||
import "../../../components/list/ha-list-base";
|
||||
import {
|
||||
ACTION_BUILDING_BLOCKS_GROUP,
|
||||
ACTION_COLLECTIONS,
|
||||
@@ -731,7 +731,7 @@ class DialogAddAutomationElement
|
||||
.manifests=${this._manifests}
|
||||
></ha-automation-add-from-target>`
|
||||
: html`
|
||||
<ha-list-base
|
||||
<ha-md-list
|
||||
class=${classMap({
|
||||
groups: true,
|
||||
hidden: hideCollections,
|
||||
@@ -739,7 +739,9 @@ class DialogAddAutomationElement
|
||||
})}
|
||||
>
|
||||
${this._params!.clipboardItem
|
||||
? html`<ha-list-item-button
|
||||
? html`<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
class="paste"
|
||||
@click=${this._paste}
|
||||
>
|
||||
@@ -783,7 +785,7 @@ class DialogAddAutomationElement
|
||||
slot="end"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>
|
||||
</ha-list-item-button>
|
||||
</ha-md-list-item>
|
||||
<wa-divider></wa-divider>`
|
||||
: nothing}
|
||||
${collections.map(
|
||||
@@ -797,7 +799,9 @@ class DialogAddAutomationElement
|
||||
collection.groups,
|
||||
(item) => item.key,
|
||||
(item) => html`
|
||||
<ha-list-item-button
|
||||
<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
.value=${item.key}
|
||||
.index=${collection.collectionIndex}
|
||||
@click=${this._groupSelected}
|
||||
@@ -817,12 +821,12 @@ class DialogAddAutomationElement
|
||||
${this._narrow
|
||||
? html`<ha-icon-next slot="end"></ha-icon-next>`
|
||||
: nothing}
|
||||
</ha-list-item-button>
|
||||
</ha-md-list-item>
|
||||
`
|
||||
)}
|
||||
`
|
||||
)}
|
||||
</ha-list-base>
|
||||
</ha-md-list>
|
||||
`}
|
||||
${!this._filter
|
||||
? html`
|
||||
@@ -2387,14 +2391,8 @@ class DialogAddAutomationElement
|
||||
gap: var(--ha-space-3);
|
||||
}
|
||||
|
||||
ha-list-item-button {
|
||||
--ha-row-item-padding-block: var(--ha-space-1);
|
||||
--ha-row-item-padding-inline: var(--ha-space-3);
|
||||
--ha-row-item-min-height: 40px;
|
||||
}
|
||||
ha-list-item-button::part(start),
|
||||
ha-list-item-button::part(end) {
|
||||
color: var(--ha-color-on-neutral-quiet);
|
||||
ha-md-list {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ha-automation-add-from-target,
|
||||
|
||||
+41
-39
@@ -23,12 +23,11 @@ import { stringCompare } from "../../../../common/string/compare";
|
||||
import "../../../../components/ha-floor-icon";
|
||||
import "../../../../components/ha-icon";
|
||||
import "../../../../components/ha-icon-next";
|
||||
import "../../../../components/ha-md-list";
|
||||
import "../../../../components/ha-md-list-item";
|
||||
import "../../../../components/ha-section-title";
|
||||
import "../../../../components/ha-state-icon";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/item/ha-list-item-button";
|
||||
import "../../../../components/item/ha-row-item";
|
||||
import "../../../../components/list/ha-list-base";
|
||||
import {
|
||||
getAreaDeviceLookup,
|
||||
getAreaEntityLookup,
|
||||
@@ -329,13 +328,15 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
)}</ha-section-title
|
||||
>
|
||||
${emptyFloors
|
||||
? html`<ha-row-item>
|
||||
<div slot="headline">
|
||||
${this._i18n.localize("ui.components.area-picker.no_areas")}
|
||||
</div>
|
||||
</ha-row-item>`
|
||||
? html`<ha-md-list>
|
||||
<ha-md-list-item type="text">
|
||||
<div slot="headline">
|
||||
${this._i18n.localize("ui.components.area-picker.no_areas")}
|
||||
</div>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>`
|
||||
: html`${narrow
|
||||
? html`<ha-list-base>${floorAreas}</ha-list-base>`
|
||||
? html`<ha-md-list>${floorAreas}</ha-md-list>`
|
||||
: html`<wa-tree
|
||||
@wa-selection-change=${this._handleSelectionChange}
|
||||
>${floorAreas}</wa-tree
|
||||
@@ -369,10 +370,12 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
"ui.components.label-picker.labels"
|
||||
)}</ha-section-title
|
||||
>
|
||||
<ha-list-base>
|
||||
<ha-md-list>
|
||||
${labels.map(
|
||||
(label) =>
|
||||
html`<ha-list-item-button
|
||||
html`<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
.target=${label.id}
|
||||
@click=${this._selectItem}
|
||||
class=${this._getSelectedTargetId(value) === label.id
|
||||
@@ -390,9 +393,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
${narrow
|
||||
? html`<ha-icon-next slot="end"></ha-icon-next> `
|
||||
: nothing}
|
||||
</ha-list-item-button>`
|
||||
</ha-md-list-item>`
|
||||
)}
|
||||
</ha-list-base>`;
|
||||
</ha-md-list>`;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -511,7 +514,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
"ui.panel.config.automation.editor.unassigned"
|
||||
)}</ha-section-title
|
||||
>${narrow
|
||||
? html`<ha-list-base>${items}</ha-list-base>`
|
||||
? html`<ha-md-list>${items}</ha-md-list>`
|
||||
: html`<wa-tree @wa-selection-change=${this._handleSelectionChange}>
|
||||
${items}
|
||||
</wa-tree>`} `;
|
||||
@@ -565,7 +568,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
"ui.components.target-picker.type.areas"
|
||||
)}</ha-section-title
|
||||
>
|
||||
<ha-list-base>${renderedAreas}</ha-list-base>`;
|
||||
<ha-md-list>${renderedAreas}</ha-md-list>`;
|
||||
}
|
||||
|
||||
return renderedAreas;
|
||||
@@ -614,7 +617,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
"ui.components.target-picker.type.devices"
|
||||
)}</ha-section-title
|
||||
>
|
||||
<ha-list-base>${renderedDevices}</ha-list-base>`;
|
||||
<ha-md-list>${renderedDevices}</ha-md-list>`;
|
||||
}
|
||||
|
||||
return renderedDevices;
|
||||
@@ -661,7 +664,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
"ui.components.target-picker.type.devices"
|
||||
)}</ha-section-title
|
||||
>
|
||||
<ha-list-base>${renderedDomains}</ha-list-base>`;
|
||||
<ha-md-list> ${renderedDomains} </ha-md-list>`;
|
||||
}
|
||||
|
||||
return renderedDomains;
|
||||
@@ -716,7 +719,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
"ui.components.target-picker.type.entities"
|
||||
)}</ha-section-title
|
||||
>
|
||||
<ha-list-base>${renderedEntites}</ha-list-base>`;
|
||||
<ha-md-list>${renderedEntites}</ha-md-list>`;
|
||||
}
|
||||
|
||||
return renderedEntites;
|
||||
@@ -781,14 +784,17 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
children?: TemplateResult | TemplateResult[] | typeof nothing
|
||||
) {
|
||||
if (this.narrow) {
|
||||
return html`<ha-list-item-button
|
||||
return html`<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
.target=${target}
|
||||
@click=${this._selectItem}
|
||||
.title=${label}
|
||||
>
|
||||
${icon?.("start")}
|
||||
<div slot="headline">${label}</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-button>`;
|
||||
</ha-md-list-item>`;
|
||||
}
|
||||
|
||||
return html`
|
||||
@@ -1185,7 +1191,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
await this.updateComplete;
|
||||
if (type === "label") {
|
||||
this.shadowRoot!.querySelector(
|
||||
"ha-list-item-button.selected"
|
||||
"ha-md-list-item.selected"
|
||||
)?.scrollIntoView({
|
||||
block: "center",
|
||||
});
|
||||
@@ -1410,9 +1416,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
overflow: hidden;
|
||||
}
|
||||
ha-list-item-button::part(label) {
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
font-family: var(--ha-font-family-heading);
|
||||
ha-md-list-item {
|
||||
--md-list-item-label-text-weight: var(--ha-font-weight-medium);
|
||||
--md-list-item-label-text-font: var(--ha-font-family-heading);
|
||||
}
|
||||
|
||||
.item-label {
|
||||
@@ -1456,33 +1462,29 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
ha-list-base {
|
||||
--ha-row-item-padding-inline: var(--ha-space-3);
|
||||
--ha-row-item-padding-block: var(--ha-space-1);
|
||||
--ha-row-item-min-height: 40px;
|
||||
ha-md-list {
|
||||
padding: 0;
|
||||
--md-list-item-leading-space: var(--ha-space-3);
|
||||
--md-list-item-trailing-space: var(--md-list-item-leading-space);
|
||||
--md-list-item-bottom-space: var(--ha-space-1);
|
||||
--md-list-item-top-space: var(--md-list-item-bottom-space);
|
||||
--md-list-item-supporting-text-font: var(--ha-font-size-s);
|
||||
--md-list-item-one-line-container-height: var(--ha-space-10);
|
||||
}
|
||||
|
||||
ha-list-item-button::part(end) {
|
||||
color: var(--ha-color-on-neutral-quiet);
|
||||
}
|
||||
|
||||
ha-list-item-button.selected {
|
||||
ha-md-list-item.selected {
|
||||
background-color: var(--ha-color-fill-primary-normal-active);
|
||||
--md-list-item-label-text-color: var(--ha-color-on-primary-normal);
|
||||
--icon-primary-color: var(--ha-color-on-primary-normal);
|
||||
}
|
||||
|
||||
ha-list-item-button.selected::part(headline) {
|
||||
color: var(--ha-color-on-primary-normal);
|
||||
}
|
||||
|
||||
wa-tree-item[selected],
|
||||
wa-tree-item[selected] > ha-svg-icon,
|
||||
wa-tree-item[selected] > ha-icon,
|
||||
wa-tree-item[selected] > ha-state-icon,
|
||||
wa-tree-item[selected] > ha-floor-icon,
|
||||
ha-list-item-button.selected ha-icon,
|
||||
ha-list-item-button.selected ha-svg-icon {
|
||||
ha-md-list-item.selected ha-icon,
|
||||
ha-md-list-item.selected ha-svg-icon {
|
||||
color: var(--ha-color-on-primary-normal);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,15 +12,15 @@ import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import "../../../../components/ha-md-list";
|
||||
import "../../../../components/ha-md-list-item";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import "../../../../components/ha-tooltip";
|
||||
import "../../../../components/item/ha-list-item-button";
|
||||
import "../../../../components/list/ha-list-base";
|
||||
import type { ConfigEntry } from "../../../../data/config_entries";
|
||||
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
||||
import { haStyleScrollbar } from "../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { AddAutomationElementListItem } from "../add-automation-element-dialog";
|
||||
import { haStyleScrollbar } from "../../../../resources/styles";
|
||||
import { getTargetIcon } from "../target/get_target_icon";
|
||||
|
||||
type Target = [string, string | undefined, string | undefined];
|
||||
@@ -103,12 +103,17 @@ export class HaAutomationAddItems extends LitElement {
|
||||
|
||||
return html`
|
||||
<div class="items-title">${title}</div>
|
||||
<ha-list-base>
|
||||
<ha-md-list>
|
||||
${repeat(
|
||||
items,
|
||||
(item) => item.key,
|
||||
(item) => html`
|
||||
<ha-list-item-button .value=${item.key} @click=${this._selected}>
|
||||
<ha-md-list-item
|
||||
interactive
|
||||
type="button"
|
||||
.value=${item.key}
|
||||
@click=${this._selected}
|
||||
>
|
||||
<div slot="headline" class=${this.target ? "item-headline" : ""}>
|
||||
${item.name}${this._renderTarget(this.target)}
|
||||
</div>
|
||||
@@ -133,7 +138,6 @@ export class HaAutomationAddItems extends LitElement {
|
||||
@click=${stopPropagation}
|
||||
></ha-svg-icon>
|
||||
<ha-tooltip
|
||||
slot="end"
|
||||
.for=${`description-tooltip-${item.key}`}
|
||||
@wa-show=${stopPropagation}
|
||||
@wa-hide=${stopPropagation}
|
||||
@@ -147,10 +151,10 @@ export class HaAutomationAddItems extends LitElement {
|
||||
class="plus"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>
|
||||
</ha-list-item-button>
|
||||
</ha-md-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-list-base>
|
||||
</ha-md-list>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -241,26 +245,24 @@ export class HaAutomationAddItems extends LitElement {
|
||||
background-color: var(--ha-color-fill-danger-quiet-resting);
|
||||
color: var(--ha-color-on-danger-normal);
|
||||
}
|
||||
.items ha-list-base {
|
||||
--ha-row-item-padding-inline: var(--ha-space-3);
|
||||
--ha-row-item-padding-block: var(--ha-space-2);
|
||||
--ha-list-gap: var(--ha-space-3);
|
||||
.items ha-md-list {
|
||||
--md-list-item-two-line-container-height: var(--ha-space-12);
|
||||
--md-list-item-leading-space: var(--ha-space-3);
|
||||
--md-list-item-trailing-space: var(--md-list-item-leading-space);
|
||||
--md-list-item-bottom-space: var(--ha-space-2);
|
||||
--md-list-item-top-space: var(--md-list-item-bottom-space);
|
||||
--md-list-item-supporting-text-font: var(--ha-font-family-body);
|
||||
--ha-md-list-item-gap: var(--ha-space-3);
|
||||
gap: var(--ha-space-2);
|
||||
padding: 0 var(--ha-space-4);
|
||||
padding-bottom: max(var(--safe-area-inset-bottom), var(--ha-space-3));
|
||||
}
|
||||
.items ha-list-base ha-list-item-button {
|
||||
.items ha-md-list ha-md-list-item {
|
||||
border-radius: var(--ha-border-radius-lg);
|
||||
border: 1px solid var(--ha-color-border-neutral-quiet);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.items ha-list-base ha-list-item-button::part(start),
|
||||
.items ha-list-base ha-list-item-button::part(end) {
|
||||
color: var(--ha-color-on-neutral-quiet);
|
||||
}
|
||||
.items ha-list-base ha-list-item-button::part(end) {
|
||||
gap: var(--ha-space-3);
|
||||
.items ha-md-list {
|
||||
padding-bottom: max(var(--safe-area-inset-bottom), var(--ha-space-3));
|
||||
}
|
||||
|
||||
.items .item-headline {
|
||||
|
||||
@@ -8,6 +8,8 @@ import "../../../../components/ha-button";
|
||||
import "../../../../components/ha-dialog";
|
||||
import "../../../../components/ha-dialog-footer";
|
||||
import "../../../../components/ha-icon-button";
|
||||
import "../../../../components/ha-md-list";
|
||||
import "../../../../components/ha-md-list-item";
|
||||
import "../../../../components/ha-select-box";
|
||||
import "../../../../components/input/ha-input";
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ export default class HaAutomationConditionEditor extends LitElement {
|
||||
`
|
||||
: nothing}
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this.condition}
|
||||
@value-changed=${this._onYamlChange}
|
||||
.readOnly=${this.disabled}
|
||||
|
||||
@@ -186,6 +186,7 @@ export class HaPlatformCondition extends LitElement {
|
||||
: nothing}
|
||||
${shouldRenderDataYaml
|
||||
? html`<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.service-control.action_data"
|
||||
)}
|
||||
|
||||
@@ -36,7 +36,6 @@ import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-yaml-editor";
|
||||
import type { HaYamlEditor } from "../../../components/ha-yaml-editor";
|
||||
import type {
|
||||
AutomationConfig,
|
||||
AutomationEntity,
|
||||
@@ -122,8 +121,6 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
@query("manual-automation-editor")
|
||||
private _manualEditor?: HaManualAutomationEditor;
|
||||
|
||||
@query("ha-yaml-editor") private _yamlEditor?: HaYamlEditor;
|
||||
|
||||
private _configSubscriptions: Record<
|
||||
string,
|
||||
(config?: AutomationConfig) => void
|
||||
@@ -544,6 +541,7 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
`
|
||||
: nothing}
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
.readOnly=${this.readOnly}
|
||||
@value-changed=${this._yamlChanged}
|
||||
@@ -830,7 +828,7 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
this.blueprintConfig = config;
|
||||
this.config = newConfig;
|
||||
if (this.mode === "yaml") {
|
||||
this._yamlEditor?.setValue(this.config);
|
||||
this.renderRoot.querySelector("ha-yaml-editor")?.setValue(this.config);
|
||||
}
|
||||
this.readOnly = true;
|
||||
this.errors = undefined;
|
||||
|
||||
@@ -276,6 +276,7 @@ export class HaAutomationTrace extends LitElement {
|
||||
: this._view === "automation_config"
|
||||
? html`
|
||||
<ha-trace-config
|
||||
.hass=${this.hass}
|
||||
.trace=${this._trace}
|
||||
></ha-trace-config>
|
||||
`
|
||||
@@ -291,6 +292,7 @@ export class HaAutomationTrace extends LitElement {
|
||||
: this._view === "blueprint"
|
||||
? html`
|
||||
<ha-trace-blueprint-config
|
||||
.hass=${this.hass}
|
||||
.trace=${this._trace}
|
||||
></ha-trace-blueprint-config>
|
||||
`
|
||||
|
||||
@@ -52,6 +52,7 @@ class DialogPasteReplace extends LitElement {
|
||||
</p>
|
||||
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._params?.pastedConfig}
|
||||
read-only
|
||||
></ha-yaml-editor>
|
||||
|
||||
@@ -124,9 +124,9 @@ export const manualEditorStyles = css`
|
||||
.has-sidebar {
|
||||
--sidebar-width: min(
|
||||
max(var(--sidebar-dynamic-width), ${SIDEBAR_MIN_WIDTH}px),
|
||||
100vw - ${CONTENT_MIN_WIDTH}px - var(--ha-sidebar-width, 0px),
|
||||
100vw - ${CONTENT_MIN_WIDTH}px - var(--mdc-drawer-width, 0px),
|
||||
var(--ha-automation-editor-max-width) -
|
||||
${CONTENT_MIN_WIDTH}px - var(--ha-sidebar-width, 0px)
|
||||
${CONTENT_MIN_WIDTH}px - var(--mdc-drawer-width, 0px)
|
||||
);
|
||||
--sidebar-gap: var(--ha-space-4);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ export default class HaAutomationTriggerEditor extends LitElement {
|
||||
`
|
||||
: nothing}
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this.trigger}
|
||||
.readOnly=${this.disabled}
|
||||
@value-changed=${this._onYamlChange}
|
||||
|
||||
@@ -760,6 +760,7 @@ export default class HaAutomationTriggerRow extends LitElement {
|
||||
<ha-yaml-editor
|
||||
read-only
|
||||
disable-fullscreen
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._triggeredResult}
|
||||
></ha-yaml-editor>
|
||||
`,
|
||||
|
||||
@@ -34,6 +34,7 @@ export class HaEventTrigger extends LitElement implements TriggerElement {
|
||||
@change=${this._valueChanged}
|
||||
></ha-input>
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.triggers.type.event.event_data"
|
||||
)}
|
||||
|
||||
@@ -221,6 +221,7 @@ export class HaPlatformTrigger extends LitElement {
|
||||
: nothing}
|
||||
${shouldRenderDataYaml
|
||||
? html`<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.service-control.action_data"
|
||||
)}
|
||||
|
||||
@@ -42,6 +42,7 @@ class SupervisorFormfieldLabel extends LitElement {
|
||||
margin-right: 4px;
|
||||
margin-inline-end: 4px;
|
||||
margin-inline-start: initial;
|
||||
font-size: var(--ha-font-size-m);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: var(--ha-line-height-normal);
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiClose, mdiContentCopy, mdiDownload } from "@mdi/js";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||
@@ -92,8 +92,6 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
||||
|
||||
@state() private _config?: BackupConfig;
|
||||
|
||||
@query("div") private _copyContainer?: HTMLElement;
|
||||
|
||||
public showDialog(params: BackupOnboardingDialogParams): void {
|
||||
this._params = params;
|
||||
|
||||
@@ -480,7 +478,7 @@ class DialogBackupOnboarding extends LitElement implements HassDialog {
|
||||
private async _copyKeyToClipboard() {
|
||||
await copyToClipboard(
|
||||
this._config!.create_backup.password!,
|
||||
this._copyContainer!
|
||||
this.renderRoot.querySelector("div")!
|
||||
);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiClose, mdiContentCopy, mdiDownload } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||
import "../../../../components/ha-button";
|
||||
@@ -37,8 +37,6 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
|
||||
@state() private _newEncryptionKey?: string;
|
||||
|
||||
@query("div") private _copyContainer?: HTMLElement;
|
||||
|
||||
public showDialog(params: ChangeBackupEncryptionKeyDialogParams): void {
|
||||
this._params = params;
|
||||
this._step = STEPS[0];
|
||||
@@ -235,7 +233,10 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
}
|
||||
|
||||
private async _copyKeyToClipboard() {
|
||||
await copyToClipboard(this._newEncryptionKey, this._copyContainer!);
|
||||
await copyToClipboard(
|
||||
this._newEncryptionKey,
|
||||
this.renderRoot.querySelector("div")!
|
||||
);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
@@ -245,7 +246,10 @@ class DialogChangeBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
if (!this._params?.currentKey) {
|
||||
return;
|
||||
}
|
||||
await copyToClipboard(this._params.currentKey, this._copyContainer!);
|
||||
await copyToClipboard(
|
||||
this._params.currentKey,
|
||||
this.renderRoot.querySelector("div")!
|
||||
);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mdiContentCopy, mdiDownload } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||
import "../../../../components/ha-button";
|
||||
@@ -36,8 +36,6 @@ class DialogSetBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
|
||||
@state() private _newEncryptionKey?: string;
|
||||
|
||||
@query("div") private _copyContainer?: HTMLElement;
|
||||
|
||||
public showDialog(params: SetBackupEncryptionKeyDialogParams): void {
|
||||
this._params = params;
|
||||
this._step = STEPS[0];
|
||||
@@ -180,7 +178,10 @@ class DialogSetBackupEncryptionKey extends LitElement implements HassDialog {
|
||||
}
|
||||
|
||||
private async _copyKeyToClipboard() {
|
||||
await copyToClipboard(this._newEncryptionKey, this._copyContainer!);
|
||||
await copyToClipboard(
|
||||
this._newEncryptionKey,
|
||||
this.renderRoot.querySelector("div")!
|
||||
);
|
||||
showToast(this, {
|
||||
message: this.hass.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user