mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-08 19:54:24 +00:00
Compare commits
16 Commits
ha-form-ta
...
refactor-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab0b1a4632 | ||
|
|
aab2304d86 | ||
|
|
c013f79826 | ||
|
|
60236c2fee | ||
|
|
20d53a2659 | ||
|
|
6dbc38386c | ||
|
|
ce5a19caa8 | ||
|
|
2cda06e7a6 | ||
|
|
65485ce8c9 | ||
|
|
b73ae60cea | ||
|
|
cef35c6c23 | ||
|
|
6b9685ec9f | ||
|
|
fc9289dc05 | ||
|
|
2a2bca2a61 | ||
|
|
1eda51ddbc | ||
|
|
22738f6d77 |
@@ -57,7 +57,7 @@ Check the [webawesome documentation](https://webawesome.com/docs/components/butt
|
||||
| ---------- | ---------------------------------------------- | -------- | --------------------------------------------------------------------------------- |
|
||||
| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. |
|
||||
| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. |
|
||||
| size | "small"/"medium" | "medium" | Sets the button size. |
|
||||
| size | "small"/"medium"/"large" | "medium" | Sets the button size. |
|
||||
| loading | Boolean | false | Shows a loading indicator instead of the buttons label and disable buttons click. |
|
||||
| disabled | Boolean | false | Disables the button and prevents user interaction. |
|
||||
|
||||
|
||||
@@ -488,79 +488,6 @@ const SCHEMAS: {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Tabs",
|
||||
translations: {
|
||||
settings: "Settings",
|
||||
tab_general: "General",
|
||||
tab_appearance: "Appearance",
|
||||
name: "Name",
|
||||
entity: "Entity",
|
||||
theme: "Theme",
|
||||
state_color: "Color on state",
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: "tabs",
|
||||
name: "settings",
|
||||
tabs: [
|
||||
{
|
||||
name: "general",
|
||||
icon: "mdi:cog",
|
||||
schema: [
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{ name: "entity", required: true, selector: { entity: {} } },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "appearance",
|
||||
icon: "mdi:palette",
|
||||
schema: [
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{ name: "state_color", selector: { boolean: {} } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Tabs (compact)",
|
||||
translations: {
|
||||
settings: "Settings",
|
||||
tab_general: "General",
|
||||
tab_appearance: "Appearance",
|
||||
name: "Name",
|
||||
entity: "Entity",
|
||||
theme: "Theme",
|
||||
state_color: "Color on state",
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: "tabs",
|
||||
name: "settings",
|
||||
fill_tabs: false,
|
||||
tabs: [
|
||||
{
|
||||
name: "general",
|
||||
icon: "mdi:cog",
|
||||
schema: [
|
||||
{ name: "name", selector: { text: {} } },
|
||||
{ name: "entity", required: true, selector: { entity: {} } },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "appearance",
|
||||
icon: "mdi:palette",
|
||||
schema: [
|
||||
{ name: "theme", selector: { theme: {} } },
|
||||
{ name: "state_color", selector: { boolean: {} } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-components-ha-form")
|
||||
@@ -608,12 +535,8 @@ class DemoHaForm extends LitElement {
|
||||
.error=${info.error}
|
||||
.disabled=${this.disabled[idx]}
|
||||
.computeError=${(error) => translations[error] || error}
|
||||
.computeLabel=${(schema, _data, options) => {
|
||||
if (options?.tab) {
|
||||
return translations[`tab_${options.tab}`] || options.tab;
|
||||
}
|
||||
return translations[schema.name] || schema.name;
|
||||
}}
|
||||
.computeLabel=${(schema) =>
|
||||
translations[schema.name] || schema.name}
|
||||
.computeHelper=${() => "Helper text"}
|
||||
@value-changed=${this._handleValueChanged}
|
||||
.sampleIdx=${idx}
|
||||
|
||||
@@ -10,7 +10,7 @@ import "../../../../src/components/input/ha-input";
|
||||
import "../../../../src/components/input/ha-input-copy";
|
||||
import "../../../../src/components/input/ha-input-multi";
|
||||
import "../../../../src/components/input/ha-input-search";
|
||||
import { localizeContext } from "../../../../src/data/context";
|
||||
import { internationalizationContext } from "../../../../src/data/context/context";
|
||||
|
||||
const LOCALIZE_KEYS: Record<string, string> = {
|
||||
"ui.common.copy": "Copy",
|
||||
@@ -26,11 +26,19 @@ const LOCALIZE_KEYS: Record<string, string> = {
|
||||
export class DemoHaInput extends LitElement {
|
||||
constructor() {
|
||||
super();
|
||||
// Provides localizeContext for ha-input-copy, ha-input-multi and ha-input-search
|
||||
// Provides internationalizationContext for ha-input-copy, ha-input-multi and ha-input-search
|
||||
// eslint-disable-next-line no-new
|
||||
new ContextProvider(this, {
|
||||
context: localizeContext,
|
||||
initialValue: ((key: string) => LOCALIZE_KEYS[key] ?? key) as any,
|
||||
context: internationalizationContext,
|
||||
initialValue: {
|
||||
localize: ((key: string) => LOCALIZE_KEYS[key] ?? key) as any,
|
||||
language: "en",
|
||||
selectedLanguage: null,
|
||||
locale: {} as any,
|
||||
translationMetadata: {} as any,
|
||||
loadBackendTranslation: (async () => (key: string) => key) as any,
|
||||
loadFragmentTranslation: (async () => (key: string) => key) as any,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import { mdiArrowCollapseDown, mdiDownload } from "@mdi/js";
|
||||
import { IntersectionController } from "@lit-labs/observers/intersection-controller.js";
|
||||
import { mdiArrowCollapseDown, mdiDownload } from "@mdi/js";
|
||||
import { LitElement, type PropertyValues, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "@material/mwc-linear-progress";
|
||||
import { mdiOpenInNew } from "@mdi/js";
|
||||
import { css, html, nothing, type PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@@ -8,6 +7,7 @@ import "../../src/components/ha-button";
|
||||
import "../../src/components/ha-fade-in";
|
||||
import "../../src/components/ha-spinner";
|
||||
import "../../src/components/ha-svg-icon";
|
||||
import "../../src/components/progress/ha-progress-bar";
|
||||
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
|
||||
import "../../src/onboarding/onboarding-welcome-links";
|
||||
import { onBoardingStyles } from "../../src/onboarding/styles";
|
||||
@@ -60,7 +60,7 @@ class HaLandingPage extends LandingPageBaseElement {
|
||||
${!networkIssue && !this._supervisorError
|
||||
? html`
|
||||
<p>${this.localize("subheader")}</p>
|
||||
<mwc-linear-progress indeterminate></mwc-linear-progress>
|
||||
<ha-progress-bar indeterminate></ha-progress-bar>
|
||||
`
|
||||
: nothing}
|
||||
${networkIssue || this._networkInfoError
|
||||
|
||||
@@ -65,10 +65,8 @@
|
||||
"@material/mwc-checkbox": "0.27.0",
|
||||
"@material/mwc-dialog": "0.27.0",
|
||||
"@material/mwc-drawer": "0.27.0",
|
||||
"@material/mwc-fab": "0.27.0",
|
||||
"@material/mwc-floating-label": "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-linear-progress": "0.27.0",
|
||||
"@material/mwc-list": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
|
||||
"@material/mwc-radio": "0.27.0",
|
||||
"@material/mwc-select": "0.27.0",
|
||||
@@ -100,7 +98,7 @@
|
||||
"dialog-polyfill": "0.5.6",
|
||||
"echarts": "6.0.0",
|
||||
"element-internals-polyfill": "3.0.2",
|
||||
"fuse.js": "7.2.0",
|
||||
"fuse.js": "7.3.0",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"gulp-zopfli-green": "7.0.0",
|
||||
"hls.js": "1.6.15",
|
||||
@@ -114,7 +112,7 @@
|
||||
"lit": "3.3.2",
|
||||
"lit-html": "3.3.2",
|
||||
"luxon": "3.7.2",
|
||||
"marked": "17.0.5",
|
||||
"marked": "17.0.6",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "4.0.4",
|
||||
"object-hash": "3.0.0",
|
||||
@@ -144,7 +142,7 @@
|
||||
"@bundle-stats/plugin-webpack-filter": "4.22.0",
|
||||
"@eslint/eslintrc": "3.3.5",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@html-eslint/eslint-plugin": "0.58.1",
|
||||
"@html-eslint/eslint-plugin": "0.59.0",
|
||||
"@lokalise/node-api": "15.6.1",
|
||||
"@octokit/auth-oauth-device": "8.0.3",
|
||||
"@octokit/plugin-retry": "8.1.0",
|
||||
|
||||
@@ -21,6 +21,9 @@ export const filterNavigationPages = (
|
||||
if (page.path === "#external-app-configuration") {
|
||||
return hass.auth.external?.config.hasSettingsScreen;
|
||||
}
|
||||
if (page.adminOnly && !hass.user?.is_admin) {
|
||||
return false;
|
||||
}
|
||||
// Only show Bluetooth page if there are Bluetooth config entries
|
||||
if (page.component === "bluetooth") {
|
||||
return options.hasBluetoothConfigEntries ?? false;
|
||||
|
||||
@@ -7,7 +7,8 @@ export type LeafletModuleType = typeof import("leaflet");
|
||||
export type LeafletDrawModuleType = typeof import("leaflet-draw");
|
||||
|
||||
export const setupLeafletMap = async (
|
||||
mapElement: HTMLElement
|
||||
mapElement: HTMLElement,
|
||||
initialView?: { latitude: number; longitude: number; zoom?: number }
|
||||
): Promise<[Map, LeafletModuleType, TileLayer]> => {
|
||||
if (!mapElement.parentNode) {
|
||||
throw new Error("Cannot setup Leaflet map on disconnected element");
|
||||
@@ -32,7 +33,12 @@ export const setupLeafletMap = async (
|
||||
markerClusterStyle.setAttribute("rel", "stylesheet");
|
||||
mapElement.parentNode.appendChild(markerClusterStyle);
|
||||
|
||||
map.setView([52.3731339, 4.8903147], 13);
|
||||
if (initialView) {
|
||||
map.setView(
|
||||
[initialView.latitude, initialView.longitude],
|
||||
initialView.zoom ?? 13
|
||||
);
|
||||
}
|
||||
|
||||
const tileLayer = createTileLayer(Leaflet).addTo(map);
|
||||
|
||||
|
||||
@@ -18,15 +18,16 @@ import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { getAllGraphColors } from "../../common/color/colors";
|
||||
import { transform } from "../../common/decorators/transform";
|
||||
import type { HASSDomEvent } from "../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||
import { afterNextRender } from "../../common/util/render-status";
|
||||
import { filterXSS } from "../../common/util/xss";
|
||||
import { themesContext } from "../../data/context";
|
||||
import { uiContext } from "../../data/context/context";
|
||||
import type { Themes } from "../../data/ws-themes";
|
||||
import type { ECOption } from "../../resources/echarts/echarts";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type { HomeAssistant, HomeAssistantUI } from "../../types";
|
||||
import { isMac } from "../../util/is_mac";
|
||||
import "../chips/ha-assist-chip";
|
||||
import "../ha-icon-button";
|
||||
@@ -74,8 +75,11 @@ export class HaChartBase extends LitElement {
|
||||
public extraComponents?: any[];
|
||||
|
||||
@state()
|
||||
@consume({ context: themesContext, subscribe: true })
|
||||
_themes!: Themes;
|
||||
@consume({ context: uiContext, subscribe: true })
|
||||
@transform<HomeAssistantUI, Themes>({
|
||||
transformer: ({ themes }) => themes,
|
||||
})
|
||||
private _themes!: Themes;
|
||||
|
||||
@state() private _isZoomed = false;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
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, state } from "lit/decorators";
|
||||
import type { RenderItemFunction } from "@lit-labs/virtualizer/virtualize";
|
||||
import { mdiRestart } from "@mdi/js";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||
import type {
|
||||
@@ -12,12 +12,12 @@ import type {
|
||||
} from "../../data/history";
|
||||
import { loadVirtualizer } from "../../resources/virtualizer";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type { StateHistoryChartLine } from "./state-history-chart-line";
|
||||
import type { StateHistoryChartTimeline } from "./state-history-chart-timeline";
|
||||
import "../ha-fab";
|
||||
import "../ha-button";
|
||||
import "../ha-svg-icon";
|
||||
import "./state-history-chart-line";
|
||||
import type { StateHistoryChartLine } from "./state-history-chart-line";
|
||||
import "./state-history-chart-timeline";
|
||||
import type { StateHistoryChartTimeline } from "./state-history-chart-timeline";
|
||||
|
||||
const CANVAS_TIMELINE_ROWS_CHUNK = 10; // Split up the canvases to avoid hitting the render limit
|
||||
|
||||
@@ -150,16 +150,14 @@ export class StateHistoryCharts extends LitElement {
|
||||
this._renderHistoryItem(item, index)
|
||||
)}`}
|
||||
${this.syncCharts && this._hasZoomedCharts
|
||||
? html`<ha-fab
|
||||
slot="fab"
|
||||
? html`<ha-button
|
||||
size="large"
|
||||
class="reset-button"
|
||||
.label=${this.hass.localize(
|
||||
"ui.components.history_charts.zoom_reset"
|
||||
)}
|
||||
@click=${this._handleGlobalZoomReset}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiRestart}></ha-svg-icon>
|
||||
</ha-fab>`
|
||||
<ha-svg-icon slot="start" .path=${mdiRestart}></ha-svg-icon>
|
||||
${this.hass.localize("ui.components.history_charts.zoom_reset")}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
@@ -448,6 +446,7 @@ export class StateHistoryCharts extends LitElement {
|
||||
bottom: calc(24px + var(--safe-area-inset-bottom));
|
||||
right: calc(24px + var(--safe-area-inset-bottom));
|
||||
z-index: 1;
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -16,17 +16,17 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { STRINGS_SEPARATOR_DOT } from "../../common/const";
|
||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type {
|
||||
HASSDomCurrentTargetEvent,
|
||||
HASSDomTargetEvent,
|
||||
} from "../../common/dom/fire_event";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { stringCompare } from "../../common/string/compare";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
import { groupBy } from "../../common/util/group-by";
|
||||
import { nextRender } from "../../common/util/render-status";
|
||||
import { localeContext, localizeContext } from "../../data/context";
|
||||
import { internationalizationContext } from "../../data/context/context";
|
||||
import type { FrontendLocaleData } from "../../data/translation";
|
||||
import { haStyleScrollbar } from "../../resources/styles";
|
||||
import { loadVirtualizer } from "../../resources/virtualizer";
|
||||
@@ -112,12 +112,8 @@ const AUTO_FOCUS_ALLOWED_ACTIVE_TAGS = ["BODY", "HTML", "HOME-ASSISTANT"];
|
||||
@customElement("ha-data-table")
|
||||
export class HaDataTable extends LitElement {
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private _localize?: ContextType<typeof localizeContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: localeContext, subscribe: true })
|
||||
private _locale?: ContextType<typeof localeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n?: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@@ -528,7 +524,9 @@ export class HaDataTable extends LitElement {
|
||||
<div class="mdc-data-table__row" role="row">
|
||||
<div class="mdc-data-table__cell grows center" role="cell">
|
||||
${this.noDataText ||
|
||||
this._localize?.("ui.components.data-table.no-data") ||
|
||||
this._i18n?.localize?.(
|
||||
"ui.components.data-table.no-data"
|
||||
) ||
|
||||
"No data"}
|
||||
</div>
|
||||
</div>
|
||||
@@ -542,8 +540,8 @@ export class HaDataTable extends LitElement {
|
||||
@scroll=${this._saveScrollPos}
|
||||
.items=${this._groupData(
|
||||
this._filteredData,
|
||||
this._localize,
|
||||
this._locale,
|
||||
this._i18n?.localize,
|
||||
this._i18n?.locale,
|
||||
this.appendRow,
|
||||
this.groupColumn,
|
||||
this.groupOrder,
|
||||
@@ -713,7 +711,7 @@ export class HaDataTable extends LitElement {
|
||||
this._sortColumns[this.sortColumn],
|
||||
this.sortDirection,
|
||||
this.sortColumn,
|
||||
this._locale?.language
|
||||
this._i18n?.locale?.language
|
||||
)
|
||||
: filteredData;
|
||||
|
||||
@@ -893,8 +891,8 @@ export class HaDataTable extends LitElement {
|
||||
|
||||
const groupedData = this._groupData(
|
||||
this._filteredData,
|
||||
this._localize,
|
||||
this._locale,
|
||||
this._i18n?.localize,
|
||||
this._i18n?.locale,
|
||||
this.appendRow,
|
||||
this.groupColumn,
|
||||
this.groupOrder,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { consume, type ContextType } from "@lit/context";
|
||||
import type { ActionDetail } from "@material/mwc-list";
|
||||
import { mdiCalendarToday } from "@mdi/js";
|
||||
import "cally";
|
||||
import type { HassConfig } from "home-assistant-js-websocket/dist/types";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, queryAll, state } from "lit/decorators";
|
||||
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
|
||||
@@ -12,16 +13,16 @@ import {
|
||||
formatDateYear,
|
||||
formatISODateOnly,
|
||||
} from "../../common/datetime/format_date";
|
||||
import { transform } from "../../common/decorators/transform";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import {
|
||||
configContext,
|
||||
localeContext,
|
||||
localizeContext,
|
||||
} from "../../data/context";
|
||||
internationalizationContext,
|
||||
} from "../../data/context/context";
|
||||
import { TimeZone } from "../../data/translation";
|
||||
import { MobileAwareMixin } from "../../mixins/mobile-aware-mixin";
|
||||
import { haStyleScrollbar } from "../../resources/styles";
|
||||
import type { ValueChangedEvent } from "../../types";
|
||||
import type { HomeAssistantConfig, ValueChangedEvent } from "../../types";
|
||||
import "../chips/ha-chip-set";
|
||||
import "../chips/ha-filter-chip";
|
||||
import type { HaFilterChip } from "../chips/ha-filter-chip";
|
||||
@@ -48,16 +49,15 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
public timePicker = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: localeContext, subscribe: true })
|
||||
private locale!: ContextType<typeof localeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: configContext, subscribe: true })
|
||||
private hassConfig!: ContextType<typeof configContext>;
|
||||
@transform<HomeAssistantConfig, HassConfig>({
|
||||
transformer: ({ config }) => config,
|
||||
})
|
||||
private _hassConfig!: HassConfig;
|
||||
|
||||
/** used to show month in calendar-range header */
|
||||
@state() private _pickerMonth?: string;
|
||||
@@ -87,12 +87,20 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
? formatCallyDateRange(
|
||||
this.startDate,
|
||||
this.endDate,
|
||||
this.locale,
|
||||
this.hassConfig
|
||||
this._i18n?.locale,
|
||||
this._hassConfig
|
||||
)
|
||||
: undefined;
|
||||
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
|
||||
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
|
||||
this._pickerMonth = formatDateMonth(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._pickerYear = formatDateYear(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
|
||||
if (this.timePicker && this.startDate && this.endDate) {
|
||||
this._timeValue = {
|
||||
@@ -144,12 +152,12 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
<div class="range">
|
||||
<calendar-range
|
||||
.value=${this._dateValue}
|
||||
.locale=${this.locale.language}
|
||||
.locale=${this._i18n.locale.language}
|
||||
.focusedDate=${this._focusDate}
|
||||
@focusday=${this._focusChanged}
|
||||
@change=${this._handleChange}
|
||||
show-outside-days
|
||||
.firstDayOfWeek=${firstWeekdayIndex(this.locale)}
|
||||
.firstDayOfWeek=${firstWeekdayIndex(this._i18n.locale)}
|
||||
>
|
||||
<ha-icon-button-prev
|
||||
tabindex="-1"
|
||||
@@ -162,7 +170,7 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
<ha-icon-button
|
||||
@click=${this._focusToday}
|
||||
.path=${mdiCalendarToday}
|
||||
.label=${this.localize("ui.dialogs.date-picker.today")}
|
||||
.label=${this._i18n.localize("ui.dialogs.date-picker.today")}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
<ha-icon-button-next
|
||||
@@ -176,9 +184,9 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
<div class="times">
|
||||
<ha-time-input
|
||||
.value=${`${this._timeValue.from.hours}:${this._timeValue.from.minutes}`}
|
||||
.locale=${this.locale}
|
||||
.locale=${this._i18n.locale}
|
||||
@value-changed=${this._handleChangeTime}
|
||||
.label=${this.localize(
|
||||
.label=${this._i18n.localize(
|
||||
"ui.components.date-range-picker.time_from"
|
||||
)}
|
||||
id="from"
|
||||
@@ -187,9 +195,9 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
></ha-time-input>
|
||||
<ha-time-input
|
||||
.value=${`${this._timeValue.to.hours}:${this._timeValue.to.minutes}`}
|
||||
.locale=${this.locale}
|
||||
.locale=${this._i18n.locale}
|
||||
@value-changed=${this._handleChangeTime}
|
||||
.label=${this.localize(
|
||||
.label=${this._i18n.localize(
|
||||
"ui.components.date-range-picker.time_to"
|
||||
)}
|
||||
id="to"
|
||||
@@ -203,19 +211,33 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
</div>
|
||||
<div class="footer">
|
||||
<ha-button appearance="plain" @click=${this._cancel}
|
||||
>${this.localize("ui.common.cancel")}</ha-button
|
||||
>${this._i18n.localize("ui.common.cancel")}</ha-button
|
||||
>
|
||||
<ha-button .disabled=${!this._dateValue} @click=${this._save}
|
||||
>${this.localize("ui.components.date-range-picker.select")}</ha-button
|
||||
>${this._i18n.localize(
|
||||
"ui.components.date-range-picker.select"
|
||||
)}</ha-button
|
||||
>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
private _focusToday() {
|
||||
const date = new Date();
|
||||
this._focusDate = formatISODateOnly(date, this.locale, this.hassConfig);
|
||||
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
|
||||
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
|
||||
this._focusDate = formatISODateOnly(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._pickerMonth = formatDateMonth(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._pickerYear = formatDateYear(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
}
|
||||
|
||||
private _cancel() {
|
||||
@@ -255,12 +277,12 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.locale.time_zone === TimeZone.server) {
|
||||
if (this._i18n.locale.time_zone === TimeZone.server) {
|
||||
startDate = new Date(
|
||||
new TZDate(startDate, this.hassConfig.time_zone).getTime()
|
||||
new TZDate(startDate, this._hassConfig.time_zone).getTime()
|
||||
);
|
||||
endDate = new Date(
|
||||
new TZDate(endDate, this.hassConfig.time_zone).getTime()
|
||||
new TZDate(endDate, this._hassConfig.time_zone).getTime()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -286,8 +308,16 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
|
||||
|
||||
private _focusChanged(ev: CustomEvent<Date>) {
|
||||
const date = ev.detail;
|
||||
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
|
||||
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
|
||||
this._pickerMonth = formatDateMonth(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._pickerYear = formatDateYear(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._focusDate = undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiCalendar } from "@mdi/js";
|
||||
import "cally";
|
||||
import { isThisYear } from "date-fns";
|
||||
import type { HassConfig } from "home-assistant-js-websocket/dist/types";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@@ -14,12 +15,13 @@ import {
|
||||
formatShortDateTime,
|
||||
formatShortDateTimeWithYear,
|
||||
} from "../../common/datetime/format_date_time";
|
||||
import { transform } from "../../common/decorators/transform";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import {
|
||||
configContext,
|
||||
localeContext,
|
||||
localizeContext,
|
||||
} from "../../data/context";
|
||||
internationalizationContext,
|
||||
} from "../../data/context/context";
|
||||
import type { HomeAssistantConfig } from "../../types";
|
||||
import "../ha-bottom-sheet";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-icon-button-next";
|
||||
@@ -43,16 +45,15 @@ const EXTENDED_RANGE_KEYS: DateRange[] = [
|
||||
@customElement("ha-date-range-picker")
|
||||
export class HaDateRangePicker extends LitElement {
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: localeContext, subscribe: true })
|
||||
private locale!: ContextType<typeof localeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: configContext, subscribe: true })
|
||||
private hassConfig!: ContextType<typeof configContext>;
|
||||
@transform<HomeAssistantConfig, HassConfig>({
|
||||
transformer: ({ config }) => config,
|
||||
})
|
||||
private _hassConfig!: HassConfig;
|
||||
|
||||
@property({ attribute: false }) public startDate!: Date;
|
||||
|
||||
@@ -117,8 +118,8 @@ export class HaDateRangePicker extends LitElement {
|
||||
this._ranges = {};
|
||||
rangeKeys.forEach((key) => {
|
||||
this._ranges![
|
||||
this.localize(`ui.components.date-range-picker.ranges.${key}`)
|
||||
] = calcDateRange(this.locale, this.hassConfig, key);
|
||||
this._i18n.localize(`ui.components.date-range-picker.ranges.${key}`)
|
||||
] = calcDateRange(this._i18n.locale, this._hassConfig, key);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -140,41 +141,43 @@ export class HaDateRangePicker extends LitElement {
|
||||
.value=${(isThisYear(this.startDate)
|
||||
? formatShortDateTime(
|
||||
this.startDate,
|
||||
this.locale,
|
||||
this.hassConfig
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
)
|
||||
: formatShortDateTimeWithYear(
|
||||
this.startDate,
|
||||
this.locale,
|
||||
this.hassConfig
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
)) +
|
||||
(window.innerWidth >= 459 ? " - " : " - \n") +
|
||||
(isThisYear(this.endDate)
|
||||
? formatShortDateTime(
|
||||
this.endDate,
|
||||
this.locale,
|
||||
this.hassConfig
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
)
|
||||
: formatShortDateTimeWithYear(
|
||||
this.endDate,
|
||||
this.locale,
|
||||
this.hassConfig
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
))}
|
||||
.label=${this.localize(
|
||||
.label=${this._i18n.localize(
|
||||
"ui.components.date-range-picker.start_date"
|
||||
) +
|
||||
" - " +
|
||||
this.localize("ui.components.date-range-picker.end_date")}
|
||||
this._i18n.localize(
|
||||
"ui.components.date-range-picker.end_date"
|
||||
)}
|
||||
.disabled=${this.disabled}
|
||||
readonly
|
||||
></ha-textarea>
|
||||
<ha-icon-button-prev
|
||||
.label=${this.localize("ui.common.previous")}
|
||||
.label=${this._i18n.localize("ui.common.previous")}
|
||||
@click=${this._handlePrev}
|
||||
>
|
||||
</ha-icon-button-prev>
|
||||
<ha-icon-button-next
|
||||
.label=${this.localize("ui.common.next")}
|
||||
.label=${this._i18n.localize("ui.common.next")}
|
||||
@click=${this._handleNext}
|
||||
>
|
||||
</ha-icon-button-next>`
|
||||
@@ -182,7 +185,7 @@ export class HaDateRangePicker extends LitElement {
|
||||
@click=${this._openPicker}
|
||||
.disabled=${this.disabled}
|
||||
id="field"
|
||||
.label=${this.localize(
|
||||
.label=${this._i18n.localize(
|
||||
"ui.components.date-range-picker.select_date_range"
|
||||
)}
|
||||
.path=${mdiCalendar}
|
||||
@@ -290,8 +293,8 @@ export class HaDateRangePicker extends LitElement {
|
||||
this.startDate,
|
||||
this.endDate,
|
||||
forward,
|
||||
this.locale,
|
||||
this.hassConfig
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this.startDate = start;
|
||||
this.endDate = end;
|
||||
|
||||
@@ -2,6 +2,7 @@ import "@home-assistant/webawesome/dist/components/divider/divider";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiBackspace, mdiCalendarToday } from "@mdi/js";
|
||||
import "cally";
|
||||
import type { HassConfig } from "home-assistant-js-websocket/dist/types";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import {
|
||||
@@ -10,12 +11,13 @@ import {
|
||||
formatDateYear,
|
||||
formatISODateOnly,
|
||||
} from "../../common/datetime/format_date";
|
||||
import { transform } from "../../common/decorators/transform";
|
||||
import {
|
||||
configContext,
|
||||
localeContext,
|
||||
localizeContext,
|
||||
} from "../../data/context";
|
||||
internationalizationContext,
|
||||
} from "../../data/context/context";
|
||||
import { DialogMixin } from "../../dialogs/dialog-mixin";
|
||||
import type { HomeAssistantConfig } from "../../types";
|
||||
import "../ha-button";
|
||||
import type { DatePickerDialogParams } from "../ha-date-input";
|
||||
import "../ha-dialog";
|
||||
@@ -40,16 +42,15 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
|
||||
LitElement
|
||||
) {
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: localeContext, subscribe: true })
|
||||
private locale!: ContextType<typeof localeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: configContext, subscribe: true })
|
||||
private hassConfig!: ContextType<typeof configContext>;
|
||||
@transform<HomeAssistantConfig, HassConfig>({
|
||||
transformer: ({ config }) => config,
|
||||
})
|
||||
private _hassConfig!: HassConfig;
|
||||
|
||||
@state() private _value?: {
|
||||
year: string;
|
||||
@@ -74,14 +75,26 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
|
||||
? new Date(`${this.params.value.split("T")[0]}T00:00:00`)
|
||||
: new Date();
|
||||
|
||||
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
|
||||
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
|
||||
this._pickerYear = formatDateYear(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._pickerMonth = formatDateMonth(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
|
||||
this._value = this.params.value
|
||||
? {
|
||||
year: this._pickerYear,
|
||||
title: formatDateShort(date, this.locale, this.hassConfig),
|
||||
dateString: formatISODateOnly(date, this.locale, this.hassConfig),
|
||||
title: formatDateShort(date, this._i18n.locale, this._hassConfig),
|
||||
dateString: formatISODateOnly(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
),
|
||||
}
|
||||
: undefined;
|
||||
}
|
||||
@@ -95,7 +108,7 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
|
||||
open
|
||||
width="small"
|
||||
.headerTitle=${this._value?.title ||
|
||||
this.localize("ui.dialogs.date-picker.title")}
|
||||
this._i18n.localize("ui.dialogs.date-picker.title")}
|
||||
.headerSubtitle=${this._value?.year}
|
||||
header-subtitle-position="above"
|
||||
>
|
||||
@@ -103,7 +116,7 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
|
||||
? html`
|
||||
<ha-icon-button
|
||||
.path=${mdiBackspace}
|
||||
.label=${this.localize("ui.dialogs.date-picker.clear")}
|
||||
.label=${this._i18n.localize("ui.dialogs.date-picker.clear")}
|
||||
slot="headerActionItems"
|
||||
@click=${this._clear}
|
||||
></ha-icon-button>
|
||||
@@ -131,7 +144,7 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
|
||||
<ha-icon-button
|
||||
@click=${this._setToday}
|
||||
.path=${mdiCalendarToday}
|
||||
.label=${this.localize("ui.dialogs.date-picker.today")}
|
||||
.label=${this._i18n.localize("ui.dialogs.date-picker.today")}
|
||||
></ha-icon-button>
|
||||
</div>
|
||||
<ha-icon-button-next tabindex="-1" slot="next"></ha-icon-button-next>
|
||||
@@ -143,10 +156,10 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
|
||||
slot="secondaryAction"
|
||||
@click=${this.closeDialog}
|
||||
>
|
||||
${this.localize("ui.common.cancel")}
|
||||
${this._i18n.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button slot="primaryAction" @click=${this._setValue}>
|
||||
${this.localize("ui.common.ok")}
|
||||
${this._i18n.localize("ui.common.ok")}
|
||||
</ha-button>
|
||||
</ha-dialog-footer>
|
||||
</ha-dialog>`;
|
||||
@@ -164,23 +177,39 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
|
||||
? new Date(`${value.split("T")[0]}T00:00:00`)
|
||||
: new Date();
|
||||
this._value = {
|
||||
year: formatDateYear(date, this.locale, this.hassConfig),
|
||||
title: formatDateShort(date, this.locale, this.hassConfig),
|
||||
year: formatDateYear(date, this._i18n.locale, this._hassConfig),
|
||||
title: formatDateShort(date, this._i18n.locale, this._hassConfig),
|
||||
dateString:
|
||||
value || formatISODateOnly(date, this.locale, this.hassConfig),
|
||||
value || formatISODateOnly(date, this._i18n.locale, this._hassConfig),
|
||||
};
|
||||
|
||||
if (setFocusDay) {
|
||||
this._focusDate = this._value.dateString;
|
||||
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
|
||||
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
|
||||
this._pickerMonth = formatDateMonth(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._pickerYear = formatDateYear(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _focusChanged(ev: CustomEvent<Date>) {
|
||||
const date = ev.detail;
|
||||
this._pickerMonth = formatDateMonth(date, this.locale, this.hassConfig);
|
||||
this._pickerYear = formatDateYear(date, this.locale, this.hassConfig);
|
||||
this._pickerMonth = formatDateMonth(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._pickerYear = formatDateYear(
|
||||
date,
|
||||
this._i18n.locale,
|
||||
this._hassConfig
|
||||
);
|
||||
this._focusDate = undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { caseInsensitiveStringCompare } from "../../common/string/compare";
|
||||
import { fullEntitiesContext } from "../../data/context";
|
||||
import { fullEntitiesContext } from "../../data/context/context";
|
||||
import type { DeviceAutomation } from "../../data/device/device_automation";
|
||||
import {
|
||||
deviceAutomationsEqual,
|
||||
|
||||
@@ -27,7 +27,7 @@ export type Appearance = "accent" | "filled" | "outlined" | "plain";
|
||||
* @cssprop --ha-button-height - The height of the button.
|
||||
* @cssprop --ha-button-border-radius - The border radius of the button. defaults to `var(--ha-border-radius-pill)`.
|
||||
*
|
||||
* @attr {("small"|"medium")} size - Sets the button size.
|
||||
* @attr {("small"|"medium"|"large")} size - Sets the button size.
|
||||
* @attr {("brand"|"neutral"|"danger"|"warning"|"success")} variant - Sets the button color variant. "primary" is default.
|
||||
* @attr {("accent"|"filled"|"plain")} appearance - Sets the button appearance.
|
||||
* @attr {boolean} loading - shows a loading indicator instead of the buttons label and disable buttons click.
|
||||
@@ -62,6 +62,7 @@ export class HaButton extends Button {
|
||||
transition: background-color var(--ha-animation-duration-fast)
|
||||
ease-out;
|
||||
text-wrap: wrap;
|
||||
box-shadow: var(--ha-button-box-shadow);
|
||||
}
|
||||
|
||||
:host([size="small"]) .button {
|
||||
@@ -73,6 +74,14 @@ export class HaButton extends Button {
|
||||
--wa-form-control-padding-inline: var(--ha-space-3);
|
||||
}
|
||||
|
||||
:host([size="large"]) .button {
|
||||
--wa-form-control-height: var(
|
||||
--ha-button-height,
|
||||
var(--button-height, 48px)
|
||||
);
|
||||
font-size: var(--ha-font-size-l);
|
||||
}
|
||||
|
||||
:host([variant="brand"]) {
|
||||
--button-color-fill-normal-active: var(
|
||||
--ha-color-fill-primary-normal-active
|
||||
|
||||
@@ -7,7 +7,7 @@ import memoizeOne from "memoize-one";
|
||||
import { computeCssColor, THEME_COLORS } from "../common/color/compute-color";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import type { LocalizeKeys } from "../common/translations/localize";
|
||||
import { localizeContext } from "../data/context";
|
||||
import { internationalizationContext } from "../data/context/context";
|
||||
import type { UiColorExtraOption } from "../data/selector";
|
||||
import type { ValueChangedEvent } from "../types";
|
||||
import "./ha-combo-box-item";
|
||||
@@ -55,8 +55,8 @@ export class HaColorPicker extends LitElement {
|
||||
@property({ type: Boolean }) public required = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
render() {
|
||||
const effectiveValue = this.value ?? this.defaultColor ?? "";
|
||||
@@ -73,7 +73,7 @@ export class HaColorPicker extends LitElement {
|
||||
.rowRenderer=${this._rowRenderer}
|
||||
.valueRenderer=${this._valueRenderer}
|
||||
@value-changed=${this._valueChanged}
|
||||
.notFoundLabel=${this.localize?.(
|
||||
.notFoundLabel=${this._i18n?.localize?.(
|
||||
"ui.components.color-picker.no_colors_found"
|
||||
)}
|
||||
.getAdditionalItems=${this._getAdditionalItems}
|
||||
@@ -103,7 +103,7 @@ export class HaColorPicker extends LitElement {
|
||||
{
|
||||
id: searchString,
|
||||
primary:
|
||||
this.localize?.("ui.components.color-picker.custom_color") ||
|
||||
this._i18n?.localize?.("ui.components.color-picker.custom_color") ||
|
||||
"Custom color",
|
||||
secondary: searchString,
|
||||
},
|
||||
@@ -130,14 +130,15 @@ export class HaColorPicker extends LitElement {
|
||||
const items: PickerComboBoxItem[] = [];
|
||||
|
||||
const defaultSuffix =
|
||||
this.localize?.("ui.components.color-picker.default") || "Default";
|
||||
this._i18n?.localize?.("ui.components.color-picker.default") ||
|
||||
"Default";
|
||||
|
||||
const addDefaultSuffix = (label: string, isDefault: boolean) =>
|
||||
isDefault && defaultSuffix ? `${label} (${defaultSuffix})` : label;
|
||||
|
||||
if (includeNone) {
|
||||
const noneLabel =
|
||||
this.localize?.("ui.components.color-picker.none") || "None";
|
||||
this._i18n?.localize?.("ui.components.color-picker.none") || "None";
|
||||
items.push({
|
||||
id: "none",
|
||||
primary: addDefaultSuffix(noneLabel, defaultColor === "none"),
|
||||
@@ -147,7 +148,7 @@ export class HaColorPicker extends LitElement {
|
||||
|
||||
if (includeState) {
|
||||
const stateLabel =
|
||||
this.localize?.("ui.components.color-picker.state") || "State";
|
||||
this._i18n?.localize?.("ui.components.color-picker.state") || "State";
|
||||
items.push({
|
||||
id: "state",
|
||||
primary: addDefaultSuffix(stateLabel, defaultColor === "state"),
|
||||
@@ -170,7 +171,7 @@ export class HaColorPicker extends LitElement {
|
||||
|
||||
Array.from(THEME_COLORS).forEach((color) => {
|
||||
const themeLabel =
|
||||
this.localize?.(
|
||||
this._i18n?.localize?.(
|
||||
`ui.components.color-picker.colors.${color}` as LocalizeKeys
|
||||
) || color;
|
||||
items.push({
|
||||
@@ -227,7 +228,7 @@ export class HaColorPicker extends LitElement {
|
||||
return html`
|
||||
<ha-svg-icon slot="start" .path=${mdiInvertColorsOff}></ha-svg-icon>
|
||||
<span slot="headline">
|
||||
${this.localize?.("ui.components.color-picker.none") || "None"}
|
||||
${this._i18n?.localize?.("ui.components.color-picker.none") || "None"}
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
@@ -235,7 +236,8 @@ export class HaColorPicker extends LitElement {
|
||||
return html`
|
||||
<ha-svg-icon slot="start" .path=${mdiPalette}></ha-svg-icon>
|
||||
<span slot="headline">
|
||||
${this.localize?.("ui.components.color-picker.state") || "State"}
|
||||
${this._i18n?.localize?.("ui.components.color-picker.state") ||
|
||||
"State"}
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
@@ -243,7 +245,7 @@ export class HaColorPicker extends LitElement {
|
||||
const extraOption = this.extraOptions?.find((o) => o.value === value);
|
||||
const label =
|
||||
extraOption?.label ||
|
||||
this.localize?.(
|
||||
this._i18n?.localize?.(
|
||||
`ui.components.color-picker.colors.${value}` as LocalizeKeys
|
||||
) ||
|
||||
value;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { ifDefined } from "lit/directives/if-defined";
|
||||
import type { HASSDomEvent } from "../common/dom/fire_event";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { withViewTransition } from "../common/util/view-transition";
|
||||
import { localizeContext } from "../data/context";
|
||||
import { internationalizationContext } from "../data/context/context";
|
||||
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
import "./ha-dialog-header";
|
||||
@@ -123,13 +123,13 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
|
||||
@query(".body") public bodyContainer!: HTMLDivElement;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
// disabled till iOS app fix the "focus_element" implementation
|
||||
// @state()
|
||||
// @consume({ context: authContext, subscribe: true })
|
||||
// private auth?: ContextType<typeof authContext>;
|
||||
// @consume({ context: configContext, subscribe: true })
|
||||
// private _hassConfig?: ContextType<typeof configContext>;
|
||||
|
||||
@state()
|
||||
private _bodyScrolled = false;
|
||||
@@ -184,7 +184,7 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
|
||||
<slot name="headerNavigationIcon" slot="navigationIcon">
|
||||
<ha-icon-button
|
||||
data-dialog="close"
|
||||
.label=${this.localize?.("ui.common.close") ?? "Close"}
|
||||
.label=${this._i18n?.localize("ui.common.close") ?? "Close"}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
</slot>
|
||||
@@ -222,13 +222,13 @@ export class HaDialog extends ScrollableFadeMixin(LitElement) {
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
// disabled till iOS app fix the "focus_element" implementation
|
||||
// if (this.auth?.external && isIosApp(this.auth.external)) {
|
||||
// if (this._hassConfig?.auth.external && isIosApp(this._hassConfig.auth.external)) {
|
||||
// const element = this.querySelector("[autofocus]");
|
||||
// if (element !== null) {
|
||||
// if (!element.id) {
|
||||
// element.id = "ha-dialog-autofocus";
|
||||
// }
|
||||
// this.auth.external.fireMessage({
|
||||
// this._hassConfig.auth.external.fireMessage({
|
||||
// type: "focus_element",
|
||||
// payload: {
|
||||
// element_id: element.id,
|
||||
|
||||
@@ -3,11 +3,10 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import {
|
||||
authContext,
|
||||
configContext,
|
||||
connectionContext,
|
||||
themesContext,
|
||||
} from "../data/context";
|
||||
uiContext,
|
||||
} from "../data/context/context";
|
||||
import {
|
||||
DEFAULT_DOMAIN_ICON,
|
||||
domainIcon,
|
||||
@@ -38,12 +37,8 @@ export class HaDomainIcon extends LitElement {
|
||||
private _connection?: ContextType<typeof connectionContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: themesContext, subscribe: true })
|
||||
private _themes?: ContextType<typeof themesContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: authContext, subscribe: true })
|
||||
private _auth?: ContextType<typeof authContext>;
|
||||
@consume({ context: uiContext, subscribe: true })
|
||||
private _hassUi?: ContextType<typeof uiContext>;
|
||||
|
||||
protected render() {
|
||||
if (this.icon) {
|
||||
@@ -59,8 +54,8 @@ export class HaDomainIcon extends LitElement {
|
||||
}
|
||||
|
||||
const icon = domainIcon(
|
||||
this._connection,
|
||||
this._hassConfig,
|
||||
this._connection.connection,
|
||||
this._hassConfig.config,
|
||||
this.domain,
|
||||
this.deviceClass,
|
||||
this.state
|
||||
@@ -86,9 +81,9 @@ export class HaDomainIcon extends LitElement {
|
||||
{
|
||||
domain: this.domain!,
|
||||
type: "icon",
|
||||
darkOptimized: this._themes?.darkMode,
|
||||
darkOptimized: this._hassUi?.themes.darkMode,
|
||||
},
|
||||
this._auth?.data.hassUrl
|
||||
this._hassConfig?.auth.data.hassUrl
|
||||
);
|
||||
return html`
|
||||
<img
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { FabBase } from "@material/mwc-fab/mwc-fab-base";
|
||||
import { styles } from "@material/mwc-fab/mwc-fab.css";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { css } from "lit";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
|
||||
@customElement("ha-fab")
|
||||
export class HaFab extends FabBase {
|
||||
protected firstUpdated(changedProperties) {
|
||||
super.firstUpdated(changedProperties);
|
||||
this.style.setProperty("--mdc-theme-secondary", "var(--primary-color)");
|
||||
}
|
||||
|
||||
static override styles = [
|
||||
styles,
|
||||
css`
|
||||
:host {
|
||||
--mdc-typography-button-text-transform: none;
|
||||
--mdc-typography-button-font-size: var(--ha-font-size-l);
|
||||
--mdc-typography-button-font-family: var(--ha-font-family-body);
|
||||
--mdc-typography-button-font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
:host .mdc-fab--extended {
|
||||
border-radius: var(
|
||||
--ha-button-border-radius,
|
||||
var(--ha-border-radius-pill)
|
||||
);
|
||||
}
|
||||
:host .mdc-fab.mdc-fab--extended .ripple {
|
||||
border-radius: var(
|
||||
--ha-button-border-radius,
|
||||
var(--ha-border-radius-pill)
|
||||
);
|
||||
}
|
||||
:host .mdc-fab--extended .mdc-fab__icon {
|
||||
margin-inline-start: -8px;
|
||||
margin-inline-end: 12px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
:disabled {
|
||||
--mdc-theme-secondary: var(--disabled-text-color);
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
`,
|
||||
// safari workaround - must be explicit
|
||||
mainWindow.document.dir === "rtl"
|
||||
? css`
|
||||
:host .mdc-fab--extended .mdc-fab__icon {
|
||||
direction: rtl;
|
||||
}
|
||||
`
|
||||
: css``,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-fab": HaFab;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import { mdiDelete, mdiFileUpload } from "@mdi/js";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
@@ -12,6 +11,7 @@ import type { HomeAssistant } from "../types";
|
||||
import { bytesToString } from "../util/bytes-to-string";
|
||||
import "./ha-button";
|
||||
import "./ha-icon-button";
|
||||
import "./progress/ha-progress-bar";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@@ -100,10 +100,11 @@ export class HaFileUpload extends LitElement {
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
<mwc-linear-progress
|
||||
<ha-progress-bar
|
||||
.indeterminate=${!this.progress}
|
||||
.progress=${this.progress ? this.progress / 100 : undefined}
|
||||
></mwc-linear-progress>
|
||||
.value=${this.progress}
|
||||
loading
|
||||
></ha-progress-bar>
|
||||
</div>`
|
||||
: html`<label
|
||||
for=${this.value ? "" : "input"}
|
||||
@@ -319,7 +320,7 @@ export class HaFileUpload extends LitElement {
|
||||
--mdc-button-outline-color: var(--primary-color);
|
||||
--ha-icon-button-size: 24px;
|
||||
}
|
||||
mwc-linear-progress {
|
||||
ha-progress-bar {
|
||||
width: 100%;
|
||||
padding: 8px 32px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -9,7 +9,7 @@ import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { navigate } from "../common/navigate";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import { labelsContext } from "../data/context";
|
||||
import { labelsContext } from "../data/context/context";
|
||||
import type { LabelRegistryEntry } from "../data/label/label_registry";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
|
||||
@@ -38,17 +38,6 @@ export const computeInitialHaFormData = (
|
||||
// Only add expandable data if it's required or any of its children have initial values.
|
||||
data[field.name] = expandableData;
|
||||
}
|
||||
} else if (field.type === "tabs") {
|
||||
const tabsData: Record<string, unknown> = {};
|
||||
for (const tab of field.tabs) {
|
||||
Object.assign(tabsData, computeInitialHaFormData(tab.schema));
|
||||
}
|
||||
const flattenTabs = field.flatten ?? !field.name;
|
||||
if (flattenTabs) {
|
||||
Object.assign(data, tabsData);
|
||||
} else if (field.required || Object.keys(tabsData).length) {
|
||||
data[field.name] = tabsData;
|
||||
}
|
||||
} else if (!field.required) {
|
||||
// Do nothing.
|
||||
} else if (field.type === "boolean") {
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-icon";
|
||||
import "../ha-svg-icon";
|
||||
import "../ha-tab-group";
|
||||
import "../ha-tab-group-tab";
|
||||
import "./ha-form";
|
||||
import type { HaForm } from "./ha-form";
|
||||
import type {
|
||||
HaFormDataContainer,
|
||||
HaFormElement,
|
||||
HaFormSchema,
|
||||
HaFormTabsSchema,
|
||||
} from "./types";
|
||||
|
||||
@customElement("ha-form-tabs")
|
||||
export class HaFormTabs extends LitElement implements HaFormElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false }) public data!: HaFormDataContainer;
|
||||
|
||||
@property({ attribute: false }) public schema!: HaFormTabsSchema;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ attribute: false }) public computeLabel?: (
|
||||
schema: HaFormSchema,
|
||||
data?: HaFormDataContainer,
|
||||
options?: { path?: string[]; tab?: string }
|
||||
) => string;
|
||||
|
||||
@property({ attribute: false }) public computeHelper?: (
|
||||
schema: HaFormSchema,
|
||||
options?: { path?: string[] }
|
||||
) => string;
|
||||
|
||||
@property({ attribute: false }) public localizeValue?: (
|
||||
key: string
|
||||
) => string;
|
||||
|
||||
@state() private _activeTab?: string;
|
||||
|
||||
private _handleTabShow = (ev: CustomEvent<{ name: string }>) => {
|
||||
const name = ev.detail?.name;
|
||||
if (name !== undefined) {
|
||||
this._activeTab = name;
|
||||
}
|
||||
};
|
||||
|
||||
protected willUpdate(changedProps: Map<PropertyKey, unknown>): void {
|
||||
super.willUpdate(changedProps);
|
||||
if (changedProps.has("schema") && this.schema.tabs.length) {
|
||||
const first = this.schema.tabs[0]!.name;
|
||||
if (
|
||||
this._activeTab === undefined ||
|
||||
!this.schema.tabs.some((t) => t.name === this._activeTab)
|
||||
) {
|
||||
this._activeTab = first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public reportValidity(): boolean {
|
||||
const forms = this.renderRoot.querySelectorAll<HaForm>("ha-form");
|
||||
let valid = true;
|
||||
forms.forEach((form) => {
|
||||
if (!form.reportValidity()) {
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
return valid;
|
||||
}
|
||||
|
||||
private _computeLabel = (
|
||||
schema: HaFormSchema,
|
||||
data?: HaFormDataContainer,
|
||||
options?: { path?: string[] }
|
||||
) => {
|
||||
if (!this.computeLabel) {
|
||||
return undefined;
|
||||
}
|
||||
return this.computeLabel(schema, data, {
|
||||
...options,
|
||||
path: [...(options?.path || []), this.schema.name],
|
||||
});
|
||||
};
|
||||
|
||||
private _computeHelper = (
|
||||
schema: HaFormSchema,
|
||||
options?: { path?: string[] }
|
||||
) => {
|
||||
if (!this.computeHelper) {
|
||||
return undefined;
|
||||
}
|
||||
return this.computeHelper(schema, {
|
||||
...options,
|
||||
path: [...(options?.path || []), this.schema.name],
|
||||
});
|
||||
};
|
||||
|
||||
private _tabTitle(tabName: string): string {
|
||||
if (!this.computeLabel) {
|
||||
return tabName;
|
||||
}
|
||||
return (
|
||||
this.computeLabel(this.schema, this.data, {
|
||||
path: [...(this.schema.name ? [this.schema.name] : [])],
|
||||
tab: tabName,
|
||||
}) ?? tabName
|
||||
);
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const tabs = this.schema.tabs;
|
||||
if (!tabs.length) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const active = this._activeTab ?? tabs[0]!.name;
|
||||
const fillTabs = this.schema.fill_tabs !== false;
|
||||
|
||||
return html`
|
||||
<ha-tab-group ?fill-tabs=${fillTabs} @wa-tab-show=${this._handleTabShow}>
|
||||
${tabs.map(
|
||||
(tab) => html`
|
||||
<ha-tab-group-tab
|
||||
slot="nav"
|
||||
.panel=${tab.name}
|
||||
.active=${active === tab.name}
|
||||
>
|
||||
${tab.icon
|
||||
? html`<ha-icon .icon=${tab.icon}></ha-icon>`
|
||||
: tab.iconPath
|
||||
? html`<ha-svg-icon .path=${tab.iconPath}></ha-svg-icon>`
|
||||
: nothing}
|
||||
${this._tabTitle(tab.name)}
|
||||
</ha-tab-group-tab>
|
||||
`
|
||||
)}
|
||||
</ha-tab-group>
|
||||
<div class="panels">
|
||||
${tabs.map((tab) => {
|
||||
const hidden = active !== tab.name;
|
||||
return html`
|
||||
<div class="panel" ?hidden=${hidden}>
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${this.data}
|
||||
.schema=${tab.schema}
|
||||
.disabled=${this.disabled}
|
||||
.computeLabel=${this._computeLabel}
|
||||
.computeHelper=${this._computeHelper}
|
||||
.localizeValue=${this.localizeValue}
|
||||
></ha-form>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
}
|
||||
.panels {
|
||||
padding-top: var(--ha-space-4);
|
||||
}
|
||||
.panel[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
:host ha-form {
|
||||
display: block;
|
||||
}
|
||||
ha-tab-group {
|
||||
display: block;
|
||||
}
|
||||
ha-tab-group-tab ha-icon,
|
||||
ha-tab-group-tab ha-svg-icon {
|
||||
flex-shrink: 0;
|
||||
margin-inline-end: var(--ha-space-2);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-form-tabs": HaFormTabs;
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ const LOAD_ELEMENTS = {
|
||||
float: () => import("./ha-form-float"),
|
||||
grid: () => import("./ha-form-grid"),
|
||||
expandable: () => import("./ha-form-expandable"),
|
||||
tabs: () => import("./ha-form-tabs"),
|
||||
integer: () => import("./ha-form-integer"),
|
||||
multi_select: () => import("./ha-form-multi_select"),
|
||||
positive_time_period_dict: () =>
|
||||
|
||||
@@ -14,8 +14,7 @@ export type HaFormSchema =
|
||||
| HaFormSelector
|
||||
| HaFormGridSchema
|
||||
| HaFormExpandableSchema
|
||||
| HaFormOptionalActionsSchema
|
||||
| HaFormTabsSchema;
|
||||
| HaFormOptionalActionsSchema;
|
||||
|
||||
export interface HaFormBaseSchema {
|
||||
name: string;
|
||||
@@ -55,26 +54,6 @@ export interface HaFormOptionalActionsSchema extends HaFormBaseSchema {
|
||||
schema: readonly HaFormSchema[];
|
||||
}
|
||||
|
||||
/** One tab pane inside a {@link HaFormTabsSchema} (not a standalone form field). */
|
||||
export interface HaFormTabDefinition {
|
||||
name: string;
|
||||
icon?: string;
|
||||
iconPath?: string;
|
||||
schema: readonly HaFormSchema[];
|
||||
}
|
||||
|
||||
export interface HaFormTabsSchema extends HaFormBaseSchema {
|
||||
type: "tabs";
|
||||
/** When true (default), tab field values merge into the parent data object. */
|
||||
flatten?: boolean;
|
||||
/**
|
||||
* When true (default), tab labels share width equally across the tab bar.
|
||||
* Set to false for compact tabs that only use their natural width.
|
||||
*/
|
||||
fill_tabs?: boolean;
|
||||
tabs: readonly HaFormTabDefinition[];
|
||||
}
|
||||
|
||||
export interface HaFormSelector extends HaFormBaseSchema {
|
||||
type?: never;
|
||||
selector: Selector;
|
||||
@@ -125,13 +104,6 @@ export interface HaFormTimeSchema extends HaFormBaseSchema {
|
||||
}
|
||||
|
||||
// Type utility to unionize a schema array by flattening any grid schemas
|
||||
type SchemaUnionTabs<T extends readonly HaFormTabDefinition[]> =
|
||||
T[number] extends infer Tab
|
||||
? Tab extends HaFormTabDefinition
|
||||
? SchemaUnion<Tab["schema"]>
|
||||
: never
|
||||
: never;
|
||||
|
||||
export type SchemaUnion<
|
||||
SchemaArray extends readonly HaFormSchema[],
|
||||
Schema = SchemaArray[number],
|
||||
@@ -140,9 +112,7 @@ export type SchemaUnion<
|
||||
| HaFormExpandableSchema
|
||||
| HaFormOptionalActionsSchema
|
||||
? SchemaUnion<Schema["schema"]> | Schema
|
||||
: Schema extends HaFormTabsSchema
|
||||
? SchemaUnionTabs<Schema["tabs"]> | Schema
|
||||
: Schema;
|
||||
: Schema;
|
||||
|
||||
export type HaFormDataContainer = Record<string, HaFormData>;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { computeCssColor } from "../common/color/compute-color";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { labelsContext } from "../data/context";
|
||||
import { labelsContext } from "../data/context/context";
|
||||
import {
|
||||
getLabels,
|
||||
labelComboBoxKeys,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { repeat } from "lit/directives/repeat";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import { labelsContext } from "../data/context";
|
||||
import { labelsContext } from "../data/context/context";
|
||||
import type { LabelRegistryEntry } from "../data/label/label_registry";
|
||||
import { updateLabelRegistryEntry } from "../data/label/label_registry";
|
||||
import { showLabelDetailDialog } from "../panels/config/labels/show-dialog-label-detail";
|
||||
|
||||
@@ -15,7 +15,7 @@ import memoizeOne from "memoize-one";
|
||||
import { tinykeys } from "tinykeys";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||
import { localeContext, localizeContext } from "../data/context";
|
||||
import { internationalizationContext } from "../data/context/context";
|
||||
import { ScrollableFadeMixin } from "../mixins/scrollable-fade-mixin";
|
||||
import {
|
||||
multiTermSortedSearch,
|
||||
@@ -162,12 +162,8 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
@query("ha-input-search") private _searchFieldElement?: HaInputSearch;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: localeContext, subscribe: true })
|
||||
private locale!: ContextType<typeof localeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state() private _items: PickerComboBoxItem[] = [];
|
||||
|
||||
@@ -222,9 +218,9 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
const searchLabel =
|
||||
this.label ??
|
||||
(this.allowCustomValue
|
||||
? (this.localize?.("ui.components.combo-box.search_or_custom") ??
|
||||
? (this.i18n.localize?.("ui.components.combo-box.search_or_custom") ??
|
||||
"Search | Add custom value")
|
||||
: (this.localize?.("ui.common.search") ?? "Search"));
|
||||
: (this.i18n.localize?.("ui.common.search") ?? "Search"));
|
||||
|
||||
return html`<ha-input-search
|
||||
appearance="outlined"
|
||||
@@ -351,7 +347,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
return caseInsensitiveStringCompare(
|
||||
sortLabelA,
|
||||
sortLabelB,
|
||||
this.locale?.language ?? navigator.language
|
||||
this.i18n.locale?.language ?? navigator.language
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -368,7 +364,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
id: this._search,
|
||||
primary:
|
||||
this.customValueLabel ??
|
||||
this.localize?.("ui.components.combo-box.add_custom_item") ??
|
||||
this.i18n.localize?.("ui.components.combo-box.add_custom_item") ??
|
||||
"Add custom item",
|
||||
secondary: `"${this._search}"`,
|
||||
icon_path: mdiPlus,
|
||||
@@ -402,10 +398,10 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
? typeof this.notFoundLabel === "function"
|
||||
? this.notFoundLabel(this._search)
|
||||
: this.notFoundLabel ||
|
||||
this.localize?.("ui.components.combo-box.no_match") ||
|
||||
this.i18n.localize?.("ui.components.combo-box.no_match") ||
|
||||
"No matching items found"
|
||||
: this.emptyLabel ||
|
||||
this.localize?.("ui.components.combo-box.no_items") ||
|
||||
this.i18n.localize?.("ui.components.combo-box.no_items") ||
|
||||
"No items available"}</span
|
||||
>
|
||||
</ha-combo-box-item>
|
||||
@@ -497,7 +493,7 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
|
||||
id: searchString,
|
||||
primary:
|
||||
this.customValueLabel ??
|
||||
this.localize?.("ui.components.combo-box.add_custom_item") ??
|
||||
this.i18n.localize?.("ui.components.combo-box.add_custom_item") ??
|
||||
"Add custom item",
|
||||
secondary: `"${searchString}"`,
|
||||
icon_path: mdiPlus,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiClose, mdiMenuDown } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
@@ -11,9 +11,8 @@ import {
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { localizeContext } from "../data/context";
|
||||
import { internationalizationContext } from "../data/context/context";
|
||||
import { PickerMixin } from "../mixins/picker-mixin";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-combo-box-item";
|
||||
import type { HaComboBoxItem } from "./ha-combo-box-item";
|
||||
import "./ha-icon";
|
||||
@@ -34,8 +33,8 @@ export class HaPickerField extends PickerMixin(LitElement) {
|
||||
@query("ha-combo-box-item", true) public item!: HaComboBoxItem;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: HomeAssistant["localize"];
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
public async focus() {
|
||||
await this.updateComplete;
|
||||
@@ -89,7 +88,7 @@ export class HaPickerField extends PickerMixin(LitElement) {
|
||||
${this.unknown
|
||||
? html`<div slot="supporting-text" class="unknown">
|
||||
${this.unknownItemText ||
|
||||
this.localize("ui.components.combo-box.unknown_item")}
|
||||
this._i18n?.localize("ui.components.combo-box.unknown_item")}
|
||||
</div>`
|
||||
: nothing}
|
||||
${showClearIcon
|
||||
|
||||
@@ -2,7 +2,7 @@ import { consume } from "@lit/context";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fullEntitiesContext } from "../../data/context";
|
||||
import { fullEntitiesContext } from "../../data/context/context";
|
||||
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
|
||||
import type { Action } from "../../data/script";
|
||||
import { migrateAutomationAction } from "../../data/script";
|
||||
|
||||
@@ -23,7 +23,10 @@ export class HaSlider extends Slider {
|
||||
--marker-height: calc(var(--ha-slider-track-size, 4px) / 2);
|
||||
--marker-width: calc(var(--ha-slider-track-size, 4px) / 2);
|
||||
--wa-color-surface-default: var(--card-background-color);
|
||||
--wa-color-neutral-fill-normal: var(--disabled-color);
|
||||
--wa-color-neutral-fill-normal: var(
|
||||
--ha-slider-track-color,
|
||||
var(--disabled-color)
|
||||
);
|
||||
--wa-tooltip-background-color: var(
|
||||
--ha-tooltip-background-color,
|
||||
var(--secondary-background-color)
|
||||
|
||||
@@ -26,11 +26,6 @@ export class HaTabGroupTab extends Tab {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tab {
|
||||
width: var(--ha-tab-base-width, auto);
|
||||
justify-content: var(--ha-tab-base-justify-content, flex-start);
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
:host(:hover:not([disabled]):not([active])) .tab {
|
||||
color: var(--wa-color-brand-on-quiet);
|
||||
|
||||
@@ -13,10 +13,6 @@ export class HaTabGroup extends TabGroup {
|
||||
|
||||
@property({ attribute: "tab-only", type: Boolean }) tabOnly = true;
|
||||
|
||||
/** When true (default), each tab trigger grows to fill the tab row evenly. */
|
||||
@property({ type: Boolean, reflect: true, attribute: "fill-tabs" })
|
||||
fillTabs = true;
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
// Prevent the tab group from consuming Alt+Arrow and Cmd+Arrow keys,
|
||||
@@ -74,13 +70,6 @@ export class HaTabGroup extends TabGroup {
|
||||
.scroll-button::part(base):hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:host([fill-tabs]) .tab-group-top .tabs ::slotted(ha-tab-group-tab),
|
||||
:host([fill-tabs]) .tab-group-bottom .tabs ::slotted(ha-tab-group-tab) {
|
||||
flex: 1;
|
||||
--ha-tab-base-width: 100%;
|
||||
--ha-tab-base-justify-content: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
type FloorComboBoxItem,
|
||||
} from "../data/area_floor_picker";
|
||||
import { getConfigEntries, type ConfigEntry } from "../data/config_entries";
|
||||
import { labelsContext } from "../data/context";
|
||||
import { labelsContext } from "../data/context/context";
|
||||
import {
|
||||
deviceComboBoxKeys,
|
||||
getDevices,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { copyToClipboard } from "../../common/util/copy-clipboard";
|
||||
import { localizeContext } from "../../data/context";
|
||||
import { internationalizationContext } from "../../data/context/context";
|
||||
import { showToast } from "../../util/toast";
|
||||
import "../ha-button";
|
||||
import "../ha-icon-button";
|
||||
@@ -59,8 +59,8 @@ export class HaInputCopy extends LitElement {
|
||||
false;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state() private _showMasked = true;
|
||||
|
||||
@@ -90,7 +90,7 @@ export class HaInputCopy extends LitElement {
|
||||
? html`<ha-icon-button
|
||||
slot="end"
|
||||
class="toggle-unmasked"
|
||||
.label=${this.localize(
|
||||
.label=${this._i18n.localize(
|
||||
`ui.common.${this._showMasked ? "show" : "hide"}`
|
||||
)}
|
||||
@click=${this._toggleMasked}
|
||||
@@ -101,7 +101,7 @@ export class HaInputCopy extends LitElement {
|
||||
</div>
|
||||
<ha-button @click=${this._copy} appearance="plain" size="small">
|
||||
<ha-svg-icon slot="start" .path=${mdiContentCopy}></ha-svg-icon>
|
||||
${this.label || this.localize("ui.common.copy")}
|
||||
${this.label || this._i18n.localize("ui.common.copy")}
|
||||
</ha-button>
|
||||
</div>
|
||||
`;
|
||||
@@ -119,7 +119,7 @@ export class HaInputCopy extends LitElement {
|
||||
private async _copy(): Promise<void> {
|
||||
await copyToClipboard(this.value);
|
||||
showToast(this, {
|
||||
message: this.localize("ui.common.copied_clipboard"),
|
||||
message: this._i18n.localize("ui.common.copied_clipboard"),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { localizeContext } from "../../data/context";
|
||||
import { internationalizationContext } from "../../data/context/context";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import "../ha-button";
|
||||
import "../ha-icon-button";
|
||||
@@ -64,8 +64,8 @@ class HaInputMulti extends LitElement {
|
||||
public updateOnBlur = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize?: ContextType<typeof localizeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n?: ContextType<typeof internationalizationContext>;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
@@ -109,7 +109,7 @@ class HaInputMulti extends LitElement {
|
||||
.index=${index}
|
||||
slot="navigationIcon"
|
||||
.label=${this.removeLabel ??
|
||||
this.localize?.("ui.common.remove") ??
|
||||
this._i18n?.localize("ui.common.remove") ??
|
||||
"Remove"}
|
||||
@click=${this._removeItem}
|
||||
.path=${mdiDeleteOutline}
|
||||
@@ -137,10 +137,10 @@ class HaInputMulti extends LitElement {
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.addLabel ??
|
||||
(this.label
|
||||
? this.localize?.("ui.components.multi-textfield.add_item", {
|
||||
? this._i18n?.localize("ui.components.multi-textfield.add_item", {
|
||||
item: this.label,
|
||||
})
|
||||
: this.localize?.("ui.common.add")) ??
|
||||
: this._i18n?.localize("ui.common.add")) ??
|
||||
"Add"}
|
||||
</ha-button>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiMagnify } from "@mdi/js";
|
||||
import { html, type PropertyValues } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
import { localizeContext } from "../../data/context";
|
||||
import { internationalizationContext } from "../../data/context/context";
|
||||
import { HaInput } from "./ha-input";
|
||||
|
||||
/**
|
||||
@@ -18,8 +18,8 @@ import { HaInput } from "./ha-input";
|
||||
@customElement("ha-input-search")
|
||||
export class HaInputSearch extends HaInput {
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -33,9 +33,9 @@ export class HaInputSearch extends HaInput {
|
||||
if (
|
||||
!this.label &&
|
||||
!this.placeholder &&
|
||||
(!this.hasUpdated || changedProps.has("localize"))
|
||||
(!this.hasUpdated || changedProps.has("_i18n"))
|
||||
) {
|
||||
this.placeholder = this.localize("ui.common.search");
|
||||
this.placeholder = this._i18n.localize("ui.common.search");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -254,7 +254,11 @@ export class HaMap extends ReactiveElement {
|
||||
}
|
||||
this._loading = true;
|
||||
try {
|
||||
[this.leafletMap, this.Leaflet] = await setupLeafletMap(map);
|
||||
[this.leafletMap, this.Leaflet] = await setupLeafletMap(map, {
|
||||
latitude: this.hass?.config.latitude ?? 52.3731339,
|
||||
longitude: this.hass?.config.longitude ?? 4.8903147,
|
||||
zoom: this.zoom,
|
||||
});
|
||||
this._updateMapStyle();
|
||||
this.leafletMap.on("click", (ev) => {
|
||||
if (this._clickCount === 0) {
|
||||
|
||||
@@ -79,6 +79,7 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
<ha-dialog
|
||||
.hass=${this.hass}
|
||||
.open=${this._open}
|
||||
width="large"
|
||||
flexcontent
|
||||
@closed=${this.closeDialog}
|
||||
@opened=${this._dialogOpened}
|
||||
@@ -230,6 +231,8 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
--media-browser-max-height: calc(
|
||||
100vh - 65px - var(--safe-area-inset-y)
|
||||
);
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
:host(.opened) ha-media-player-browse {
|
||||
@@ -248,7 +251,6 @@ class DialogMediaPlayerBrowse extends LitElement {
|
||||
--media-browser-max-height: calc(
|
||||
100vh - 145px - var(--safe-area-inset-y)
|
||||
);
|
||||
width: 700px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ import "../entity/ha-entity-picker";
|
||||
import "../ha-alert";
|
||||
import "../ha-button";
|
||||
import "../ha-card";
|
||||
import "../ha-fab";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-list";
|
||||
import "../ha-list-item";
|
||||
@@ -446,24 +445,20 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
currentItem.media_content_id
|
||||
))
|
||||
? html`
|
||||
<ha-fab
|
||||
mini
|
||||
<ha-button
|
||||
class="fab"
|
||||
.item=${currentItem}
|
||||
@click=${this._actionClicked}
|
||||
.title=${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}`
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.label=${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}-media`
|
||||
)}
|
||||
.path=${this.action === "play"
|
||||
? mdiPlay
|
||||
: mdiPlus}
|
||||
></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
`ui.components.media-browser.${this.action}`
|
||||
)}
|
||||
</ha-fab>
|
||||
</ha-button>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
@@ -1363,11 +1358,11 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
height 0.4s,
|
||||
padding-bottom 0.4s;
|
||||
}
|
||||
ha-fab {
|
||||
.fab {
|
||||
position: absolute;
|
||||
--mdc-theme-secondary: var(--primary-color);
|
||||
bottom: -20px;
|
||||
right: 20px;
|
||||
bottom: calc(var(--ha-space-5) * -1);
|
||||
right: var(--ha-space-5);
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
:host([narrow]) .header-info ha-button {
|
||||
margin-top: 16px;
|
||||
@@ -1429,11 +1424,10 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
padding-bottom: initial;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:host([scrolled]) ha-fab {
|
||||
bottom: 0px;
|
||||
right: -24px;
|
||||
--mdc-fab-box-shadow: none;
|
||||
--mdc-theme-secondary: rgba(var(--rgb-primary-color), 0.5);
|
||||
:host([scrolled]) .fab {
|
||||
bottom: 0;
|
||||
right: calc(var(--ha-space-6) * -1);
|
||||
--ha-button-box-shadow: none;
|
||||
}
|
||||
|
||||
lit-virtualizer {
|
||||
|
||||
92
src/components/progress/ha-progress-bar.ts
Normal file
92
src/components/progress/ha-progress-bar.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import ProgressBar from "@home-assistant/webawesome/dist/components/progress-bar/progress-bar";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { css } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
/**
|
||||
* Home Assistant progress bar component
|
||||
*
|
||||
* @element ha-progress-bar
|
||||
* @extends {ProgressBar}
|
||||
*
|
||||
* @summary
|
||||
* A stylable progress bar component based on webawesome progress bar.
|
||||
* Supports regular, indeterminate, and loading states with Home Assistant theming.
|
||||
*
|
||||
* @cssprop --ha-progress-bar-indicator-color - Color of the filled progress indicator.
|
||||
* @cssprop --ha-progress-bar-track-color - Color of the progress track.
|
||||
* @cssprop --ha-progress-bar-track-height - Height of the progress track. Defaults to `16px`.
|
||||
* @cssprop --ha-progress-bar-border-radius - Border radius of the progress bar. Defaults to `var(--ha-border-radius-pill)`.
|
||||
* @cssprop --ha-progress-bar-animation-duration - Animation duration for indeterminate/loading highlight. Defaults to `2.5s`.
|
||||
*
|
||||
* @attr {boolean} loading - Shows the loading highlight animation on top of the indicator.
|
||||
* @attr {boolean} indeterminate - Shows indeterminate progress animation (inherited from ProgressBar).
|
||||
*/
|
||||
@customElement("ha-progress-bar")
|
||||
export class HaProgressBar extends ProgressBar {
|
||||
@property({ type: Boolean, reflect: true })
|
||||
loading = false;
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
ProgressBar.styles,
|
||||
css`
|
||||
:host {
|
||||
--indicator-color: var(
|
||||
--ha-progress-bar-indicator-color,
|
||||
var(--ha-color-on-primary-normal)
|
||||
);
|
||||
--track-color: var(
|
||||
--ha-progress-bar-track-color,
|
||||
var(--ha-color-fill-neutral-normal-hover)
|
||||
);
|
||||
--track-height: var(--ha-progress-bar-track-height, 16px);
|
||||
--wa-transition-slow: var(--ha-animation-duration-slow);
|
||||
}
|
||||
.progress-bar {
|
||||
border-radius: var(
|
||||
--ha-progress-bar-border-radius,
|
||||
var(--ha-border-radius-pill)
|
||||
);
|
||||
}
|
||||
@keyframes slide-highlight {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
:host([indeterminate]) .indicator {
|
||||
animation: wa-progress-indeterminate
|
||||
var(--ha-progress-bar-animation-duration, 2.5s) infinite
|
||||
cubic-bezier(0.37, 0, 0.63, 1);
|
||||
}
|
||||
:host([indeterminate]) .indicator::after,
|
||||
:host([loading]) .indicator::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
var(--ha-color-fill-primary-normal-hover) 45%,
|
||||
var(--ha-color-fill-primary-normal-active) 50%,
|
||||
var(--ha-color-fill-primary-normal-hover) 55%,
|
||||
transparent 100%
|
||||
);
|
||||
opacity: 0.4;
|
||||
animation: slide-highlight
|
||||
var(--ha-progress-bar-animation-duration, 2.5s) infinite
|
||||
cubic-bezier(0.37, 0, 0.63, 1);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-progress-bar": HaProgressBar;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import { getEntityContext } from "../../common/entity/context/get_entity_context
|
||||
import { computeRTL } from "../../common/util/compute_rtl";
|
||||
import type { AreaRegistryEntry } from "../../data/area/area_registry";
|
||||
import { getConfigEntry } from "../../data/config_entries";
|
||||
import { labelsContext } from "../../data/context";
|
||||
import { labelsContext } from "../../data/context/context";
|
||||
import type { DeviceRegistryEntry } from "../../data/device/device_registry";
|
||||
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity/entity";
|
||||
import type { FloorRegistryEntry } from "../../data/floor_registry";
|
||||
|
||||
@@ -21,7 +21,7 @@ import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||
import { slugify } from "../../common/string/slugify";
|
||||
import { getConfigEntry } from "../../data/config_entries";
|
||||
import { labelsContext } from "../../data/context";
|
||||
import { labelsContext } from "../../data/context/context";
|
||||
import { domainToName } from "../../data/integration";
|
||||
import type { LabelRegistryEntry } from "../../data/label/label_registry";
|
||||
import type { TargetType } from "../../data/target";
|
||||
|
||||
@@ -5,8 +5,10 @@ import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
|
||||
import type { Trigger } from "../../data/automation";
|
||||
import { migrateAutomationTrigger } from "../../data/automation";
|
||||
import { describeCondition, describeTrigger } from "../../data/automation_i18n";
|
||||
import { fullEntitiesContext, labelsContext } from "../../data/context";
|
||||
import { fullEntitiesContext, labelsContext } from "../../data/context/context";
|
||||
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
|
||||
import type { LabelRegistryEntry } from "../../data/label/label_registry";
|
||||
import type { LogbookEntry } from "../../data/logbook";
|
||||
@@ -24,8 +26,6 @@ import "../ha-icon-button";
|
||||
import "./hat-logbook-note";
|
||||
import type { NodeInfo } from "./hat-script-graph";
|
||||
import { traceTabStyles } from "./trace-tab-styles";
|
||||
import type { Trigger } from "../../data/automation";
|
||||
import { migrateAutomationTrigger } from "../../data/automation";
|
||||
|
||||
const TRACE_PATH_TABS = [
|
||||
"step_config",
|
||||
|
||||
@@ -15,7 +15,7 @@ import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_tim
|
||||
import { relativeTime } from "../../common/datetime/relative_time";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { toggleAttribute } from "../../common/dom/toggle_attribute";
|
||||
import { fullEntitiesContext } from "../../data/context";
|
||||
import { fullEntitiesContext } from "../../data/context/context";
|
||||
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
|
||||
import type { LogbookEntry } from "../../data/logbook";
|
||||
import type {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import type { HassConfig, HassEntity } from "home-assistant-js-websocket";
|
||||
import { ensureArray } from "../common/array/ensure-array";
|
||||
import {
|
||||
@@ -68,8 +69,22 @@ const localizeTimeString = (
|
||||
return time;
|
||||
}
|
||||
try {
|
||||
const dt = new Date("1970-01-01T" + time);
|
||||
if (chunks.length === 2 || Number(chunks[2]) === 0) {
|
||||
const hours = Number(chunks[0]);
|
||||
const minutes = Number(chunks[1]);
|
||||
const seconds = chunks.length > 2 ? Number(chunks[2]) : 0;
|
||||
// Create date in the server timezone so formatTime converts correctly
|
||||
// when the user's browser timezone differs from the HA server timezone.
|
||||
const now = new Date();
|
||||
const dt = new TZDate(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate(),
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
config.time_zone
|
||||
);
|
||||
if (chunks.length === 2 || seconds === 0) {
|
||||
return formatTime(dt, locale, config);
|
||||
}
|
||||
return formatTimeWithSeconds(dt, locale, config);
|
||||
@@ -1197,7 +1212,17 @@ const describeLegacyCondition = (
|
||||
|
||||
let hasTime = "";
|
||||
if (after !== undefined && before !== undefined) {
|
||||
hasTime = "after_before";
|
||||
if (
|
||||
typeof condition.after === "string" &&
|
||||
!condition.after.includes(".") &&
|
||||
typeof condition.before === "string" &&
|
||||
!condition.before.includes(".") &&
|
||||
condition.after > condition.before
|
||||
) {
|
||||
hasTime = "after_before_or";
|
||||
} else {
|
||||
hasTime = "after_before";
|
||||
}
|
||||
} else if (after !== undefined) {
|
||||
hasTime = "after";
|
||||
} else if (before !== undefined) {
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { createContext } from "@lit/context";
|
||||
import type { HassConfig } from "home-assistant-js-websocket";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { ConfigEntry } from "./config_entries";
|
||||
import type { EntityRegistryEntry } from "./entity/entity_registry";
|
||||
import type { LabelRegistryEntry } from "./label/label_registry";
|
||||
|
||||
export const connectionContext =
|
||||
createContext<HomeAssistant["connection"]>("connection");
|
||||
export const statesContext = createContext<HomeAssistant["states"]>("states");
|
||||
export const entitiesContext =
|
||||
createContext<HomeAssistant["entities"]>("entities");
|
||||
export const devicesContext =
|
||||
createContext<HomeAssistant["devices"]>("devices");
|
||||
export const areasContext = createContext<HomeAssistant["areas"]>("areas");
|
||||
export const localizeContext =
|
||||
createContext<HomeAssistant["localize"]>("localize");
|
||||
export const localeContext = createContext<HomeAssistant["locale"]>("locale");
|
||||
export const configContext = createContext<HassConfig>("config");
|
||||
export const themesContext = createContext<HomeAssistant["themes"]>("themes");
|
||||
export const selectedThemeContext =
|
||||
createContext<HomeAssistant["selectedTheme"]>("selectedTheme");
|
||||
export const userContext = createContext<HomeAssistant["user"]>("user");
|
||||
export const userDataContext =
|
||||
createContext<HomeAssistant["userData"]>("userData");
|
||||
export const panelsContext = createContext<HomeAssistant["panels"]>("panels");
|
||||
|
||||
export const fullEntitiesContext =
|
||||
createContext<EntityRegistryEntry[]>("extendedEntities");
|
||||
|
||||
export const floorsContext = createContext<HomeAssistant["floors"]>("floors");
|
||||
|
||||
export const labelsContext = createContext<LabelRegistryEntry[]>("labels");
|
||||
|
||||
export const configEntriesContext =
|
||||
createContext<ConfigEntry[]>("configEntries");
|
||||
|
||||
export const authContext = createContext<HomeAssistant["auth"]>("auth");
|
||||
148
src/data/context/context.ts
Normal file
148
src/data/context/context.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import { createContext } from "@lit/context";
|
||||
import type { HassConfig } from "home-assistant-js-websocket";
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantConfig,
|
||||
HomeAssistantConnection,
|
||||
HomeAssistantInternationalization,
|
||||
HomeAssistantRegistries,
|
||||
HomeAssistantUI,
|
||||
} from "../../types";
|
||||
import type { ConfigEntry } from "../config_entries";
|
||||
import type { EntityRegistryEntry } from "../entity/entity_registry";
|
||||
import type { LabelRegistryEntry } from "../label/label_registry";
|
||||
|
||||
/**
|
||||
* Entity, device, area, and floor registries
|
||||
*/
|
||||
export const registriesContext =
|
||||
createContext<HomeAssistantRegistries>("hassRegistries");
|
||||
|
||||
/**
|
||||
* Live map of all entity states, keyed by entity ID.
|
||||
*/
|
||||
export const statesContext = createContext<HomeAssistant["states"]>("states");
|
||||
|
||||
/**
|
||||
* Provides the map of all available Home Assistant services, keyed by domain.
|
||||
*/
|
||||
export const servicesContext =
|
||||
createContext<HomeAssistant["services"]>("services");
|
||||
|
||||
/**
|
||||
* i18n state: active language, locale settings, the `localize` function, translation metadata, and the
|
||||
* `loadBackendTranslation` / `loadFragmentTranslation` loaders.
|
||||
*/
|
||||
export const internationalizationContext =
|
||||
createContext<HomeAssistantInternationalization>("hassInternationalization");
|
||||
|
||||
/**
|
||||
* HTTP and WebSocket API surface: `callService`, `callApi`,
|
||||
* `callApiRaw`, `callWS`, `sendWS`, `fetchWithAuth`, and `hassUrl`.
|
||||
*/
|
||||
export const apiContext = createContext<HomeAssistantApi>("hassApi");
|
||||
|
||||
/**
|
||||
* WebSocket connection state: `connection`, `connected`, and `debugConnection`.
|
||||
*/
|
||||
export const connectionContext =
|
||||
createContext<HomeAssistantConnection>("hassConnection");
|
||||
|
||||
/**
|
||||
* UI preferences and global UI state: themes, selected theme,
|
||||
* panels, sidebar mode, kiosk mode, shortcuts, vibration, and
|
||||
* `suspendWhenHidden`.
|
||||
*/
|
||||
export const uiContext = createContext<HomeAssistantUI>("hassUi");
|
||||
|
||||
/**
|
||||
* HA core configuration together with user session data:
|
||||
* `auth`, `config` (core HA config), `user`, `userData`, and `systemData`.
|
||||
*/
|
||||
export const configContext = createContext<HomeAssistantConfig>("hassConfig");
|
||||
|
||||
/**
|
||||
* Map of all entities in the entity registry, keyed by entity ID.
|
||||
*/
|
||||
export const entitiesContext =
|
||||
createContext<HomeAssistant["entities"]>("entities");
|
||||
|
||||
/**
|
||||
* Map of all devices in the device registry, keyed by device ID.
|
||||
*/
|
||||
export const devicesContext =
|
||||
createContext<HomeAssistant["devices"]>("devices");
|
||||
|
||||
/**
|
||||
* Map of all areas in the area registry, keyed by area ID.
|
||||
*/
|
||||
export const areasContext = createContext<HomeAssistant["areas"]>("areas");
|
||||
|
||||
/**
|
||||
* Map of all floors in the floor registry, keyed by floor ID.
|
||||
*/
|
||||
export const floorsContext = createContext<HomeAssistant["floors"]>("floors");
|
||||
|
||||
// #region lazy-contexts
|
||||
|
||||
/**
|
||||
* Lazy contexts are not subscribed to by default. They are only subscribed to when a provider is consumed with at least one consumer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Lazy loaded labels registry, keyed by label ID.
|
||||
*/
|
||||
export const labelsContext = createContext<LabelRegistryEntry[]>("labels");
|
||||
|
||||
/**
|
||||
* Lazy loaded entity registry array
|
||||
*/
|
||||
export const fullEntitiesContext =
|
||||
createContext<EntityRegistryEntry[]>("extendedEntities");
|
||||
|
||||
/**
|
||||
* Lazy loaded config entries array
|
||||
*/
|
||||
export const configEntriesContext =
|
||||
createContext<ConfigEntry[]>("configEntries");
|
||||
|
||||
// #endregion lazy-contexts
|
||||
|
||||
// #region deprecated-contexts
|
||||
|
||||
/** @deprecated Use `connectionContext` instead. */
|
||||
export const connectionSingleContext =
|
||||
createContext<HomeAssistant["connection"]>("connection");
|
||||
|
||||
/** @deprecated Use `internationalizationContext` instead. */
|
||||
export const localizeContext =
|
||||
createContext<HomeAssistant["localize"]>("localize");
|
||||
|
||||
/** @deprecated Use `internationalizationContext` instead. */
|
||||
export const localeContext = createContext<HomeAssistant["locale"]>("locale");
|
||||
|
||||
/** @deprecated Use `configContext` instead. */
|
||||
export const configSingleContext = createContext<HassConfig>("config");
|
||||
|
||||
/** @deprecated Use `uiContext` instead. */
|
||||
export const themesContext = createContext<HomeAssistant["themes"]>("themes");
|
||||
|
||||
/** @deprecated Use `uiContext` instead. */
|
||||
export const selectedThemeContext =
|
||||
createContext<HomeAssistant["selectedTheme"]>("selectedTheme");
|
||||
|
||||
/** @deprecated Use `configContext` instead. */
|
||||
export const userContext = createContext<HomeAssistant["user"]>("user");
|
||||
|
||||
/** @deprecated Use `configContext` instead. */
|
||||
export const userDataContext =
|
||||
createContext<HomeAssistant["userData"]>("userData");
|
||||
|
||||
/** @deprecated Use `uiContext` instead. */
|
||||
export const panelsContext = createContext<HomeAssistant["panels"]>("panels");
|
||||
|
||||
/** @deprecated Use `configContext` instead. */
|
||||
export const authContext = createContext<HomeAssistant["auth"]>("auth");
|
||||
|
||||
// #endregion deprecated-contexts
|
||||
166
src/data/context/updateContext.ts
Normal file
166
src/data/context/updateContext.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import type {
|
||||
HomeAssistant,
|
||||
HomeAssistantApi,
|
||||
HomeAssistantConfig,
|
||||
HomeAssistantConnection,
|
||||
HomeAssistantInternationalization,
|
||||
HomeAssistantRegistries,
|
||||
HomeAssistantUI,
|
||||
} from "../../types";
|
||||
|
||||
const updateRegistries = (
|
||||
hass: HomeAssistant,
|
||||
value?: HomeAssistantRegistries
|
||||
): HomeAssistantRegistries => {
|
||||
if (
|
||||
!value ||
|
||||
value.entities !== hass.entities ||
|
||||
value.devices !== hass.devices ||
|
||||
value.areas !== hass.areas ||
|
||||
value.floors !== hass.floors
|
||||
) {
|
||||
return {
|
||||
entities: hass.entities,
|
||||
devices: hass.devices,
|
||||
areas: hass.areas,
|
||||
floors: hass.floors,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const updateInternationalization = (
|
||||
hass: HomeAssistant,
|
||||
value?: HomeAssistantInternationalization
|
||||
): HomeAssistantInternationalization => {
|
||||
if (
|
||||
!value ||
|
||||
value.localize !== hass.localize ||
|
||||
value.locale !== hass.locale ||
|
||||
value.loadBackendTranslation !== hass.loadBackendTranslation ||
|
||||
value.loadFragmentTranslation !== hass.loadFragmentTranslation ||
|
||||
value.language !== hass.language ||
|
||||
value.selectedLanguage !== hass.selectedLanguage ||
|
||||
value.translationMetadata !== hass.translationMetadata
|
||||
) {
|
||||
return {
|
||||
localize: hass.localize,
|
||||
locale: hass.locale,
|
||||
loadBackendTranslation: hass.loadBackendTranslation,
|
||||
loadFragmentTranslation: hass.loadFragmentTranslation,
|
||||
language: hass.language,
|
||||
selectedLanguage: hass.selectedLanguage,
|
||||
translationMetadata: hass.translationMetadata,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const updateApi = (
|
||||
hass: HomeAssistant,
|
||||
value?: HomeAssistantApi
|
||||
): HomeAssistantApi => {
|
||||
if (
|
||||
!value ||
|
||||
value.callService !== hass.callService ||
|
||||
value.callApi !== hass.callApi ||
|
||||
value.callApiRaw !== hass.callApiRaw ||
|
||||
value.callWS !== hass.callWS ||
|
||||
value.sendWS !== hass.sendWS ||
|
||||
value.fetchWithAuth !== hass.fetchWithAuth ||
|
||||
value.hassUrl !== hass.hassUrl
|
||||
) {
|
||||
return {
|
||||
callService: hass.callService,
|
||||
callApi: hass.callApi,
|
||||
callApiRaw: hass.callApiRaw,
|
||||
callWS: hass.callWS,
|
||||
sendWS: hass.sendWS,
|
||||
fetchWithAuth: hass.fetchWithAuth,
|
||||
hassUrl: hass.hassUrl,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const updateConnection = (
|
||||
hass: HomeAssistant,
|
||||
value?: HomeAssistantConnection
|
||||
): HomeAssistantConnection => {
|
||||
if (
|
||||
!value ||
|
||||
value.connection !== hass.connection ||
|
||||
value.connected !== hass.connected ||
|
||||
value.debugConnection !== hass.debugConnection
|
||||
) {
|
||||
return {
|
||||
connection: hass.connection,
|
||||
connected: hass.connected,
|
||||
debugConnection: hass.debugConnection,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const updateUi = (
|
||||
hass: HomeAssistant,
|
||||
value?: HomeAssistantUI
|
||||
): HomeAssistantUI => {
|
||||
if (
|
||||
!value ||
|
||||
value.themes !== hass.themes ||
|
||||
value.selectedTheme !== hass.selectedTheme ||
|
||||
value.panels !== hass.panels ||
|
||||
value.panelUrl !== hass.panelUrl ||
|
||||
value.dockedSidebar !== hass.dockedSidebar ||
|
||||
value.kioskMode !== hass.kioskMode ||
|
||||
value.enableShortcuts !== hass.enableShortcuts ||
|
||||
value.vibrate !== hass.vibrate ||
|
||||
value.suspendWhenHidden !== hass.suspendWhenHidden
|
||||
) {
|
||||
return {
|
||||
themes: hass.themes,
|
||||
selectedTheme: hass.selectedTheme,
|
||||
panels: hass.panels,
|
||||
panelUrl: hass.panelUrl,
|
||||
dockedSidebar: hass.dockedSidebar,
|
||||
kioskMode: hass.kioskMode,
|
||||
enableShortcuts: hass.enableShortcuts,
|
||||
vibrate: hass.vibrate,
|
||||
suspendWhenHidden: hass.suspendWhenHidden,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const updateConfig = (
|
||||
hass: HomeAssistant,
|
||||
value?: HomeAssistantConfig
|
||||
): HomeAssistantConfig => {
|
||||
if (
|
||||
!value ||
|
||||
value.auth !== hass.auth ||
|
||||
value.config !== hass.config ||
|
||||
value.user !== hass.user ||
|
||||
value.userData !== hass.userData ||
|
||||
value.systemData !== hass.systemData
|
||||
) {
|
||||
return {
|
||||
auth: hass.auth,
|
||||
config: hass.config,
|
||||
user: hass.user,
|
||||
userData: hass.userData,
|
||||
systemData: hass.systemData,
|
||||
};
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
export const updateHassGroups = {
|
||||
registries: updateRegistries,
|
||||
internationalization: updateInternationalization,
|
||||
api: updateApi,
|
||||
connection: updateConnection,
|
||||
ui: updateUi,
|
||||
config: updateConfig,
|
||||
};
|
||||
@@ -4,6 +4,7 @@ export interface CoreFrontendUserData {
|
||||
showAdvanced?: boolean;
|
||||
showEntityIdPicker?: boolean;
|
||||
default_panel?: string;
|
||||
apps_info_dismissed?: boolean;
|
||||
}
|
||||
|
||||
export interface SidebarFrontendUserData {
|
||||
|
||||
@@ -105,10 +105,6 @@ const generateNavigationConfigSectionCommands = (
|
||||
hass: HomeAssistant,
|
||||
filterOptions: NavigationFilterOptions = {}
|
||||
): BaseNavigationCommand[] => {
|
||||
if (!hass.user?.is_admin) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const items: NavigationInfo[] = [];
|
||||
const allPages = Object.values(configSections).flat();
|
||||
const visiblePages = filterNavigationPages(hass, allPages, filterOptions);
|
||||
|
||||
@@ -99,16 +99,6 @@ export const showConfigFlowDialog = (
|
||||
);
|
||||
}
|
||||
|
||||
if (field.type === "tabs" && options?.tab) {
|
||||
const sectionPrefix = field.name ? `sections.${field.name}.` : "";
|
||||
return (
|
||||
hass.localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.${sectionPrefix}tabs.${options.tab}.name`,
|
||||
step.description_placeholders
|
||||
) || options.tab
|
||||
);
|
||||
}
|
||||
|
||||
const prefix = options?.path?.[0] ? `sections.${options.path[0]}.` : "";
|
||||
|
||||
return (
|
||||
@@ -127,17 +117,6 @@ export const showConfigFlowDialog = (
|
||||
);
|
||||
}
|
||||
|
||||
if (field.type === "tabs" && options?.tab) {
|
||||
const sectionPrefix = field.name ? `sections.${field.name}.` : "";
|
||||
const tabDescription = hass.localize(
|
||||
`component.${step.translation_domain || step.handler}.config.step.${step.step_id}.${sectionPrefix}tabs.${options.tab}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return tabDescription
|
||||
? html`<ha-markdown breaks .content=${tabDescription}></ha-markdown>`
|
||||
: "";
|
||||
}
|
||||
|
||||
const prefix = options?.path?.[0] ? `sections.${options.path[0]}.` : "";
|
||||
|
||||
const description = hass.localize(
|
||||
|
||||
@@ -62,14 +62,14 @@ export interface FlowConfig {
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepForm,
|
||||
field: HaFormSchema,
|
||||
options: { path?: string[]; tab?: string; [key: string]: any }
|
||||
options: { path?: string[]; [key: string]: any }
|
||||
): string;
|
||||
|
||||
renderShowFormStepFieldHelper(
|
||||
hass: HomeAssistant,
|
||||
step: DataEntryFlowStepForm,
|
||||
field: HaFormSchema,
|
||||
options: { path?: string[]; tab?: string; [key: string]: any }
|
||||
options: { path?: string[]; [key: string]: any }
|
||||
): TemplateResult | string;
|
||||
|
||||
renderShowFormStepFieldError(
|
||||
|
||||
@@ -103,16 +103,6 @@ export const showOptionsFlowDialog = (
|
||||
);
|
||||
}
|
||||
|
||||
if (field.type === "tabs" && options?.tab) {
|
||||
const sectionPrefix = field.name ? `sections.${field.name}.` : "";
|
||||
return (
|
||||
hass.localize(
|
||||
`component.${configEntry.domain}.options.step.${step.step_id}.${sectionPrefix}tabs.${options.tab}.name`,
|
||||
step.description_placeholders
|
||||
) || options.tab
|
||||
);
|
||||
}
|
||||
|
||||
const prefix = options?.path?.[0] ? `sections.${options.path[0]}.` : "";
|
||||
|
||||
return (
|
||||
@@ -131,20 +121,6 @@ export const showOptionsFlowDialog = (
|
||||
);
|
||||
}
|
||||
|
||||
if (field.type === "tabs" && options?.tab) {
|
||||
const sectionPrefix = field.name ? `sections.${field.name}.` : "";
|
||||
const tabDescription = hass.localize(
|
||||
`component.${step.translation_domain || configEntry.domain}.options.step.${step.step_id}.${sectionPrefix}tabs.${options.tab}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return tabDescription
|
||||
? html`<ha-markdown
|
||||
breaks
|
||||
.content=${tabDescription}
|
||||
></ha-markdown>`
|
||||
: "";
|
||||
}
|
||||
|
||||
const prefix = options?.path?.[0] ? `sections.${options.path[0]}.` : "";
|
||||
|
||||
const description = hass.localize(
|
||||
|
||||
@@ -95,16 +95,6 @@ export const showSubConfigFlowDialog = (
|
||||
);
|
||||
}
|
||||
|
||||
if (field.type === "tabs" && options?.tab) {
|
||||
const sectionPrefix = field.name ? `sections.${field.name}.` : "";
|
||||
return (
|
||||
hass.localize(
|
||||
`component.${configEntry.domain}.config_subentries.${flowType}.step.${step.step_id}.${sectionPrefix}tabs.${options.tab}.name`,
|
||||
step.description_placeholders
|
||||
) || options.tab
|
||||
);
|
||||
}
|
||||
|
||||
const prefix = options?.path?.[0] ? `sections.${options.path[0]}.` : "";
|
||||
|
||||
return (
|
||||
@@ -123,17 +113,6 @@ export const showSubConfigFlowDialog = (
|
||||
);
|
||||
}
|
||||
|
||||
if (field.type === "tabs" && options?.tab) {
|
||||
const sectionPrefix = field.name ? `sections.${field.name}.` : "";
|
||||
const tabDescription = hass.localize(
|
||||
`component.${step.translation_domain || configEntry.domain}.config_subentries.${flowType}.step.${step.step_id}.${sectionPrefix}tabs.${options.tab}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
return tabDescription
|
||||
? html`<ha-markdown breaks .content=${tabDescription}></ha-markdown>`
|
||||
: "";
|
||||
}
|
||||
|
||||
const prefix = options?.path?.[0] ? `sections.${options.path[0]}.` : "";
|
||||
|
||||
const description = hass.localize(
|
||||
|
||||
@@ -75,17 +75,7 @@ class StepFlowForm extends LitElement {
|
||||
handleReadOnlyField(sectionField)
|
||||
),
|
||||
}
|
||||
: field.type === "tabs" && field.tabs
|
||||
? {
|
||||
...field,
|
||||
tabs: field.tabs.map((tab) => ({
|
||||
...tab,
|
||||
schema: tab.schema.map((tabField) =>
|
||||
handleReadOnlyField(tabField)
|
||||
),
|
||||
})),
|
||||
}
|
||||
: handleReadOnlyField(field)
|
||||
: handleReadOnlyField(field)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { blankBeforePercent } from "../../common/translations/blank_before_percent";
|
||||
import "../../components/ha-progress-ring";
|
||||
import "../../components/ha-spinner";
|
||||
import "../../components/progress/ha-progress-ring";
|
||||
import type { DataEntryFlowStepProgress } from "../../data/data_entry_flow";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import type { FlowConfig } from "./show-dialog-data-entry-flow";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
@@ -15,6 +14,7 @@ import "../../../components/ha-md-list";
|
||||
import "../../../components/ha-md-list-item";
|
||||
import "../../../components/ha-spinner";
|
||||
import "../../../components/ha-switch";
|
||||
import "../../../components/progress/ha-progress-bar";
|
||||
import type { BackupConfig } from "../../../data/backup";
|
||||
import { fetchBackupConfig } from "../../../data/backup";
|
||||
import { isUnavailableState } from "../../../data/entity/entity";
|
||||
@@ -191,11 +191,11 @@ class MoreInfoUpdate extends LitElement {
|
||||
${this.stateObj.attributes.in_progress
|
||||
? supportsFeature(this.stateObj, UpdateEntityFeature.PROGRESS) &&
|
||||
this.stateObj.attributes.update_percentage !== null
|
||||
? html`<mwc-linear-progress
|
||||
.progress=${this.stateObj.attributes.update_percentage / 100}
|
||||
buffer=""
|
||||
></mwc-linear-progress>`
|
||||
: html`<mwc-linear-progress indeterminate></mwc-linear-progress>`
|
||||
? html`<ha-progress-bar
|
||||
loading
|
||||
.value=${this.stateObj.attributes.update_percentage}
|
||||
></ha-progress-bar>`
|
||||
: html`<ha-progress-bar indeterminate></ha-progress-bar>`
|
||||
: nothing}
|
||||
<h3>${this.stateObj.attributes.title}</h3>
|
||||
${this._error
|
||||
@@ -521,10 +521,6 @@ class MoreInfoUpdate extends LitElement {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
mwc-linear-progress {
|
||||
margin-bottom: calc(var(--ha-space-2) * -1);
|
||||
margin-top: var(--ha-space-1);
|
||||
}
|
||||
ha-markdown {
|
||||
direction: ltr;
|
||||
padding-bottom: var(--ha-space-4);
|
||||
|
||||
@@ -57,7 +57,11 @@ import type { HomeAssistant } from "../../types";
|
||||
import { isMac } from "../../util/is_mac";
|
||||
import { showConfirmationDialog } from "../generic/show-dialog-box";
|
||||
import { showShortcutsDialog } from "../shortcuts/show-shortcuts-dialog";
|
||||
import type { QuickBarParams, QuickBarSection } from "./show-dialog-quick-bar";
|
||||
import {
|
||||
effectiveQuickBarMode,
|
||||
type QuickBarParams,
|
||||
type QuickBarSection,
|
||||
} from "./show-dialog-quick-bar";
|
||||
|
||||
const SEPARATOR = "________";
|
||||
|
||||
@@ -100,7 +104,7 @@ export class QuickBar extends LitElement {
|
||||
this._translationsLoaded = true;
|
||||
}
|
||||
this._initialize();
|
||||
this._selectedSection = params.mode;
|
||||
this._selectedSection = effectiveQuickBarMode(this.hass.user, params.mode);
|
||||
this._showHint = params.showHint ?? false;
|
||||
|
||||
this._relatedResult = params.contextItem ? params.related : undefined;
|
||||
@@ -656,8 +660,10 @@ export class QuickBar extends LitElement {
|
||||
|
||||
private _generateActionCommandsMemoized = memoizeOne(generateActionCommands);
|
||||
|
||||
private _createFuseIndex = (states, keys: FuseWeightedKey[]) =>
|
||||
Fuse.createIndex(keys, states);
|
||||
private _createFuseIndex = (
|
||||
states: PickerComboBoxItem[],
|
||||
keys: FuseWeightedKey[]
|
||||
) => Fuse.createIndex(keys, states);
|
||||
|
||||
private _fuseIndexes = {
|
||||
entity: memoizeOne((states: PickerComboBoxItem[]) =>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { ItemType, RelatedResult } from "../../data/search";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import { closeDialog } from "../make-dialog-manager";
|
||||
|
||||
export type QuickBarSection =
|
||||
@@ -22,6 +23,20 @@ export interface QuickBarParams {
|
||||
related?: RelatedResult;
|
||||
}
|
||||
|
||||
/** Non-admin users cannot scope the bar to command, device, or area (those sections are admin-only). */
|
||||
export const effectiveQuickBarMode = (
|
||||
user: HomeAssistant["user"],
|
||||
mode?: QuickBarSection
|
||||
): QuickBarSection | undefined => {
|
||||
if (mode && user?.is_admin) {
|
||||
return mode;
|
||||
}
|
||||
if (mode === "command" || mode === "device" || mode === "area") {
|
||||
return undefined;
|
||||
}
|
||||
return mode;
|
||||
};
|
||||
|
||||
export const loadQuickBar = () => import("./ha-quick-bar");
|
||||
|
||||
export const showQuickBar = (
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import {
|
||||
mdiAutoFix,
|
||||
mdiLifebuoy,
|
||||
@@ -19,6 +18,7 @@ import "../../components/ha-icon-next";
|
||||
import "../../components/ha-md-list";
|
||||
import "../../components/ha-md-list-item";
|
||||
import "../../components/ha-spinner";
|
||||
import "../../components/progress/ha-progress-bar";
|
||||
import { fetchBackupInfo } from "../../data/backup";
|
||||
import type { BackupManagerState } from "../../data/backup_manager";
|
||||
import {
|
||||
@@ -120,9 +120,7 @@ class DialogRestart extends LitElement {
|
||||
<div class="action-loader">
|
||||
${this._loadingBackupInfo
|
||||
? html`<ha-fade-in .delay=${250}>
|
||||
<mwc-linear-progress
|
||||
.indeterminate=${true}
|
||||
></mwc-linear-progress>
|
||||
<ha-progress-bar indeterminate></ha-progress-bar>
|
||||
</ha-fade-in>`
|
||||
: nothing}
|
||||
</div>
|
||||
@@ -464,7 +462,8 @@ class DialogRestart extends LitElement {
|
||||
padding: 24px;
|
||||
}
|
||||
.action-loader {
|
||||
height: 4px;
|
||||
--ha-progress-bar-track-height: 4px;
|
||||
--ha-progress-bar-border-radius: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -5,7 +5,7 @@ import type { LocalizeKeys } from "../../common/translations/localize";
|
||||
import "../../components/ha-alert";
|
||||
import "../../components/ha-dialog";
|
||||
import "../../components/ha-svg-icon";
|
||||
import { localizeContext } from "../../data/context";
|
||||
import { internationalizationContext } from "../../data/context/context";
|
||||
import { isMac } from "../../util/is_mac";
|
||||
import { DialogMixin } from "../dialog-mixin";
|
||||
|
||||
@@ -168,8 +168,8 @@ const _SHORTCUTS: Section[] = [
|
||||
@customElement("dialog-shortcuts")
|
||||
class DialogShortcuts extends DialogMixin(LitElement) {
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
private _renderShortcut(
|
||||
shortcutKeys: ShortcutString[],
|
||||
@@ -183,13 +183,15 @@ class DialogShortcuts extends DialogMixin(LitElement) {
|
||||
>${shortcutKey === CTRL_CMD
|
||||
? isMac
|
||||
? "⌘"
|
||||
: this.localize("ui.dialogs.shortcuts.keys.ctrl")
|
||||
: this._i18n.localize("ui.dialogs.shortcuts.keys.ctrl")
|
||||
: typeof shortcutKey === "string"
|
||||
? shortcutKey
|
||||
: this.localize(shortcutKey.shortcutTranslationKey)}</span
|
||||
: this._i18n.localize(
|
||||
shortcutKey.shortcutTranslationKey
|
||||
)}</span
|
||||
>`
|
||||
)}
|
||||
${this.localize(descriptionKey)}
|
||||
${this._i18n.localize(descriptionKey)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -198,12 +200,12 @@ class DialogShortcuts extends DialogMixin(LitElement) {
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
.headerTitle=${this.localize("ui.dialogs.shortcuts.title")}
|
||||
.headerTitle=${this._i18n.localize("ui.dialogs.shortcuts.title")}
|
||||
>
|
||||
<div class="content">
|
||||
${_SHORTCUTS.map(
|
||||
(section) => html`
|
||||
<h3>${this.localize(section.titleTranslationKey)}</h3>
|
||||
<h3>${this._i18n.localize(section.titleTranslationKey)}</h3>
|
||||
<div class="items">
|
||||
${section.items.map((item) => {
|
||||
if ("shortcut" in item) {
|
||||
@@ -213,7 +215,7 @@ class DialogShortcuts extends DialogMixin(LitElement) {
|
||||
);
|
||||
}
|
||||
return html`<p>
|
||||
${this.localize((item as Text).textTranslationKey)}
|
||||
${this._i18n.localize((item as Text).textTranslationKey)}
|
||||
</p>`;
|
||||
})}
|
||||
</div>
|
||||
@@ -222,9 +224,9 @@ class DialogShortcuts extends DialogMixin(LitElement) {
|
||||
</div>
|
||||
|
||||
<ha-alert slot="footer">
|
||||
${this.localize("ui.dialogs.shortcuts.enable_shortcuts_hint", {
|
||||
${this._i18n.localize("ui.dialogs.shortcuts.enable_shortcuts_hint", {
|
||||
user_profile: html`<a href="/profile/general#shortcuts"
|
||||
>${this.localize(
|
||||
>${this._i18n.localize(
|
||||
"ui.dialogs.shortcuts.enable_shortcuts_hint_user_profile"
|
||||
)}</a
|
||||
>`,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import { mdiDotsVertical, mdiRestart } from "@mdi/js";
|
||||
import { css, html, LitElement, type TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../../components/ha-progress-ring";
|
||||
import "../../components/ha-spinner";
|
||||
import "../../components/progress/ha-progress-ring";
|
||||
import { ON, UNAVAILABLE } from "../../data/entity/entity";
|
||||
import {
|
||||
updateCanInstall,
|
||||
|
||||
@@ -303,7 +303,6 @@ export const provideHass = (
|
||||
debugConnection: false,
|
||||
kioskMode: false,
|
||||
suspendWhenHidden: false,
|
||||
moreInfoEntityId: null as any,
|
||||
// @ts-ignore
|
||||
async callService(domain, service, data) {
|
||||
if (data && "entity_id" in data) {
|
||||
|
||||
@@ -199,6 +199,7 @@ class HassSubpage extends LitElement {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: var(--ha-space-2);
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
:host([narrow]) #fab.tabs {
|
||||
bottom: calc(84px + var(--safe-area-inset-bottom, 0px));
|
||||
|
||||
@@ -34,6 +34,8 @@ export interface PageNavigation {
|
||||
not_component?: string | string[];
|
||||
core?: boolean;
|
||||
advancedOnly?: boolean;
|
||||
/** Hide from non-admin users in filtered navigation and quick bar. */
|
||||
adminOnly?: boolean;
|
||||
iconPath?: string;
|
||||
iconSecondaryPath?: string;
|
||||
iconViewBox?: string;
|
||||
@@ -420,6 +422,7 @@ export class HassTabsSubpage extends LitElement {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: var(--ha-space-2);
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
:host([narrow][show-tabs]) #fab {
|
||||
bottom: calc(84px + var(--safe-area-inset-bottom, 0px));
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import type { Auth } from "home-assistant-js-websocket";
|
||||
import {
|
||||
createConnection,
|
||||
@@ -16,6 +15,8 @@ import {
|
||||
} from "../common/auth/token_storage";
|
||||
import { applyThemesOnElement } from "../common/dom/apply_themes_on_element";
|
||||
import type { HASSDomEvent } from "../common/dom/fire_event";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
import { navigate } from "../common/navigate";
|
||||
import {
|
||||
addSearchParam,
|
||||
extractSearchParam,
|
||||
@@ -33,19 +34,18 @@ import {
|
||||
onboardIntegrationStep,
|
||||
} from "../data/onboarding";
|
||||
import { subscribeUser } from "../data/ws-user";
|
||||
import { makeDialogManager } from "../dialogs/make-dialog-manager";
|
||||
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
|
||||
import { HassElement } from "../state/hass-element";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { storeState } from "../util/ha-pref-storage";
|
||||
import { registerServiceWorker } from "../util/register-service-worker";
|
||||
import "../components/progress/ha-progress-bar";
|
||||
import "./onboarding-analytics";
|
||||
import "./onboarding-create-user";
|
||||
import "./onboarding-loading";
|
||||
import "./onboarding-welcome";
|
||||
import "./onboarding-welcome-links";
|
||||
import { makeDialogManager } from "../dialogs/make-dialog-manager";
|
||||
import { navigate } from "../common/navigate";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
|
||||
type OnboardingEvent =
|
||||
| {
|
||||
@@ -126,9 +126,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
};
|
||||
|
||||
protected render() {
|
||||
return html`<mwc-linear-progress
|
||||
.progress=${this._progress}
|
||||
></mwc-linear-progress>
|
||||
return html`<ha-progress-bar .value=${this._progress}></ha-progress-bar>
|
||||
<ha-card>
|
||||
<div class="card-content">${this._renderStep()}</div>
|
||||
</ha-card>
|
||||
@@ -318,7 +316,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
history.replaceState(null, "", location.pathname);
|
||||
await this._connectHass(auth);
|
||||
const currentStep = steps.findIndex((stp) => !stp.done);
|
||||
const singelStepProgress = 1 / steps.length;
|
||||
const singelStepProgress = 100 / steps.length;
|
||||
this._progress = currentStep * singelStepProgress + singelStepProgress;
|
||||
} else {
|
||||
this._init = true;
|
||||
@@ -333,7 +331,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
}
|
||||
|
||||
private _handleProgress(ev: HASSDomEvent<OnboardingProgressEvent>) {
|
||||
const stepSize = 1 / this._steps!.length;
|
||||
const stepSize = 100 / this._steps!.length;
|
||||
if (ev.detail.increase) {
|
||||
this._progress += ev.detail.increase * stepSize;
|
||||
}
|
||||
@@ -355,7 +353,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
this._init = false;
|
||||
this._restoring = stepResult.result?.restore;
|
||||
if (!this._restoring) {
|
||||
this._progress = 0.25;
|
||||
this._progress = 25;
|
||||
} else {
|
||||
navigate(
|
||||
`${location.pathname}?${addSearchParam({ page: `restore_backup${this._restoring === "cloud" ? "_cloud" : ""}` })}`
|
||||
@@ -364,7 +362,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
} else if (stepResult.type === "user") {
|
||||
const result = stepResult.result as OnboardingResponses["user"];
|
||||
this._loading = true;
|
||||
this._progress = 0.5;
|
||||
this._progress = 50;
|
||||
enableWrite();
|
||||
try {
|
||||
const auth = await getAuth({
|
||||
@@ -381,10 +379,10 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
this._loading = false;
|
||||
}
|
||||
} else if (stepResult.type === "core_config") {
|
||||
this._progress = 0.75;
|
||||
this._progress = 75;
|
||||
// We do nothing
|
||||
} else if (stepResult.type === "analytics") {
|
||||
this._progress = 1;
|
||||
this._progress = 100;
|
||||
// We do nothing
|
||||
} else if (stepResult.type === "integration") {
|
||||
this._loading = true;
|
||||
@@ -505,7 +503,9 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
||||
.card-content {
|
||||
padding: 32px;
|
||||
}
|
||||
mwc-linear-progress {
|
||||
ha-progress-bar {
|
||||
--ha-progress-bar-border-radius: 0;
|
||||
--ha-progress-bar-track-height: 4px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import "@material/mwc-linear-progress/mwc-linear-progress";
|
||||
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
import "../../components/ha-alert";
|
||||
import "../../components/ha-button";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
import "../../components/progress/ha-progress-bar";
|
||||
import type { BackupOnboardingInfo } from "../../data/backup_onboarding";
|
||||
import { onBoardingStyles } from "../styles";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
|
||||
@customElement("onboarding-restore-backup-status")
|
||||
class OnboardingRestoreBackupStatus extends LitElement {
|
||||
@@ -33,7 +33,7 @@ class OnboardingRestoreBackupStatus extends LitElement {
|
||||
${this.backupInfo.state === "restore_backup"
|
||||
? html`
|
||||
<div class="loading">
|
||||
<mwc-linear-progress indeterminate></mwc-linear-progress>
|
||||
<ha-progress-bar indeterminate></ha-progress-bar>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
@@ -92,7 +92,7 @@ class OnboardingRestoreBackupStatus extends LitElement {
|
||||
padding: 16px 0;
|
||||
font-size: var(--ha-font-size-l);
|
||||
}
|
||||
mwc-linear-progress {
|
||||
ha-progress-bar {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import type { CalendarOptions } from "@fullcalendar/core";
|
||||
import { Calendar } from "@fullcalendar/core";
|
||||
import allLocales from "@fullcalendar/core/locales-all";
|
||||
@@ -16,7 +17,6 @@ import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoize from "memoize-one";
|
||||
import { TZDate } from "@date-fns/tz";
|
||||
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
|
||||
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
|
||||
import { useAmPm } from "../../common/datetime/use_am_pm";
|
||||
@@ -25,7 +25,6 @@ import { supportsFeature } from "../../common/entity/supports-feature";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-button-toggle-group";
|
||||
import "../../components/ha-fab";
|
||||
import "../../components/ha-icon-button-next";
|
||||
import "../../components/ha-icon-button-prev";
|
||||
import type {
|
||||
@@ -218,14 +217,10 @@ export class HAFullCalendar extends LitElement {
|
||||
|
||||
<div id="calendar"></div>
|
||||
${this.addFab && this._hasMutableCalendars
|
||||
? html`<ha-fab
|
||||
slot="fab"
|
||||
.label=${this.hass.localize("ui.components.calendar.event.add")}
|
||||
extended
|
||||
@click=${this._createEvent}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>`
|
||||
? html`<ha-button size="large" slot="fab" @click=${this._createEvent}>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.hass.localize("ui.components.calendar.event.add")}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
`;
|
||||
}
|
||||
@@ -559,13 +554,14 @@ export class HAFullCalendar extends LitElement {
|
||||
--ha-icon-button-size: 32px;
|
||||
}
|
||||
|
||||
ha-fab {
|
||||
ha-button[slot="fab"] {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
inset-inline-end: 16px;
|
||||
bottom: var(--ha-space-4);
|
||||
right: var(--ha-space-4);
|
||||
inset-inline-end: var(--ha-space-4);
|
||||
inset-inline-start: initial;
|
||||
z-index: 1;
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
|
||||
#calendar {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import type {
|
||||
@@ -10,11 +11,10 @@ import type {
|
||||
SelectionChangedEvent,
|
||||
SortingChangedEvent,
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-help-tooltip";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-icon-overflow-menu";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import type { ApplicationCredential } from "../../../data/application_credential";
|
||||
import {
|
||||
deleteApplicationCredential,
|
||||
@@ -30,7 +30,6 @@ import type { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
import { showAddApplicationCredentialDialog } from "./show-dialog-add-application-credential";
|
||||
import { storage } from "../../../common/decorators/storage";
|
||||
|
||||
@customElement("ha-config-application-credentials")
|
||||
export class HaConfigApplicationCredentials extends LitElement {
|
||||
@@ -201,16 +200,16 @@ export class HaConfigApplicationCredentials extends LitElement {
|
||||
</ha-help-tooltip>
|
||||
`}
|
||||
</div>
|
||||
<ha-fab
|
||||
<ha-button
|
||||
slot="fab"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.application_credentials.picker.add_application_credential"
|
||||
)}
|
||||
extended
|
||||
size="large"
|
||||
@click=${this._addApplicationCredential}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.application_credentials.picker.add_application_credential"
|
||||
)}
|
||||
</ha-button>
|
||||
</hass-tabs-subpage-data-table>
|
||||
`;
|
||||
}
|
||||
|
||||
179
src/panels/config/apps/ha-config-apps-info.ts
Normal file
179
src/panels/config/apps/ha-config-apps-info.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import { mdiOpenInNew, mdiPuzzle } from "@mdi/js";
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { saveFrontendUserData } from "../../../data/frontend";
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
|
||||
@customElement("ha-config-apps-info")
|
||||
class HaConfigAppsInfo extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: "is-wide", type: Boolean }) public isWide = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<hass-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
back-path="/config"
|
||||
.header=${this.hass.localize("ui.panel.config.dashboard.apps.main")}
|
||||
>
|
||||
<div class="content">
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<div class="header">
|
||||
<ha-svg-icon class="icon" .path=${mdiPuzzle}></ha-svg-icon>
|
||||
<h1>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.apps.info.what_is_an_app"
|
||||
)}
|
||||
</h1>
|
||||
</div>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.apps.info.what_is_an_app_description"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<h2>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.apps.info.why_not_available"
|
||||
)}
|
||||
</h2>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.apps.info.why_not_available_description"
|
||||
)}
|
||||
</p>
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.apps.info.installation_hint"
|
||||
)}
|
||||
</ha-alert>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-button
|
||||
appearance="plain"
|
||||
href=${documentationUrl(this.hass, "/apps/")}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass.localize("ui.panel.config.apps.info.learn_more")}
|
||||
<ha-svg-icon slot="icon" .path=${mdiOpenInNew}></ha-svg-icon>
|
||||
</ha-button>
|
||||
<ha-button @click=${this._dismiss} variant="danger">
|
||||
${this.hass.localize("ui.panel.config.apps.info.dismiss")}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _dismiss(): Promise<void> {
|
||||
try {
|
||||
await saveFrontendUserData(this.hass.connection, "core", {
|
||||
...this.hass.userData,
|
||||
apps_info_dismissed: true,
|
||||
});
|
||||
} catch (err) {
|
||||
showAlertDialog(this, { text: (err as Error).message });
|
||||
return;
|
||||
}
|
||||
navigate("/config", { replace: true });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.content {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: var(--ha-space-4);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ha-space-4);
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: var(--ha-space-4);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-3);
|
||||
margin-bottom: var(--ha-space-4);
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex-shrink: 0;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 var(--ha-space-3);
|
||||
font-size: var(--ha-font-size-l);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 var(--ha-space-3);
|
||||
line-height: var(--ha-line-height-normal);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
ha-alert {
|
||||
display: block;
|
||||
margin-top: var(--ha-space-2);
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--ha-space-2);
|
||||
padding: var(--ha-space-2);
|
||||
border-top: var(--ha-border-width-sm) solid var(--divider-color);
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-config-apps-info": HaConfigAppsInfo;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { navigate } from "../../../common/navigate";
|
||||
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/input/ha-input-search";
|
||||
@@ -157,16 +157,10 @@ export class HaConfigAppsInstalled extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="/config/apps/available">
|
||||
<ha-fab
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.apps.installed.add_app"
|
||||
)}
|
||||
extended
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiStorePlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
</a>
|
||||
<ha-button size="large" href="/config/apps/available">
|
||||
<ha-svg-icon slot="start" .path=${mdiStorePlus}></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.apps.installed.add_app")}
|
||||
</ha-button>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
@@ -270,7 +264,7 @@ export class HaConfigAppsInstalled extends LitElement {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ha-fab {
|
||||
ha-button[size="large"] {
|
||||
position: fixed;
|
||||
right: calc(var(--ha-space-4) + var(--safe-area-inset-right));
|
||||
bottom: calc(var(--ha-space-4) + var(--safe-area-inset-bottom));
|
||||
@@ -279,6 +273,7 @@ export class HaConfigAppsInstalled extends LitElement {
|
||||
);
|
||||
inset-inline-start: initial;
|
||||
z-index: 1;
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import "../../../components/data-table/ha-data-table";
|
||||
import type { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||
@@ -116,13 +116,10 @@ export class HaConfigAppsRegistries extends LitElement {
|
||||
id="registry"
|
||||
has-fab
|
||||
></ha-data-table>
|
||||
<ha-fab
|
||||
.label=${this.hass.localize("ui.panel.config.apps.registries.add")}
|
||||
extended
|
||||
@click=${this._showAddRegistryDialog}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<ha-button size="large" @click=${this._showAddRegistryDialog}>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.apps.registries.add")}
|
||||
</ha-button>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
@@ -187,13 +184,14 @@ export class HaConfigAppsRegistries extends LitElement {
|
||||
ha-icon-button.delete {
|
||||
color: var(--error-color);
|
||||
}
|
||||
ha-fab {
|
||||
ha-button[size="large"] {
|
||||
position: fixed;
|
||||
right: calc(var(--ha-space-4) + var(--safe-area-inset-right));
|
||||
bottom: calc(var(--ha-space-4) + var(--safe-area-inset-bottom));
|
||||
inset-inline-end: calc(var(--ha-space-4) + var(--safe-area-inset-right));
|
||||
inset-inline-start: initial;
|
||||
z-index: 1;
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -8,14 +8,14 @@ import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||
import { extractSearchParam } from "../../../common/url/search-params";
|
||||
import "../../../components/data-table/ha-data-table";
|
||||
import type { DataTableColumnContainer } from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
import "../../../components/ha-tooltip";
|
||||
import type {
|
||||
HassioAddonInfo,
|
||||
HassioAddonsInfo,
|
||||
HassioAddonRepository,
|
||||
HassioAddonsInfo,
|
||||
} from "../../../data/hassio/addon";
|
||||
import { fetchHassioAddonsInfo } from "../../../data/hassio/addon";
|
||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||
@@ -195,13 +195,10 @@ export class HaConfigAppsRepositories extends LitElement {
|
||||
id="slug"
|
||||
has-fab
|
||||
></ha-data-table>
|
||||
<ha-fab
|
||||
.label=${this.hass.localize("ui.panel.config.apps.repositories.add")}
|
||||
extended
|
||||
@click=${this._showAddRepositoryDialog}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<ha-button size="large" @click=${this._showAddRepositoryDialog}>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.apps.repositories.add")}
|
||||
</ha-button>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
@@ -295,13 +292,14 @@ export class HaConfigAppsRepositories extends LitElement {
|
||||
ha-icon-button.delete {
|
||||
color: var(--error-color);
|
||||
}
|
||||
ha-fab {
|
||||
ha-button[size="large"] {
|
||||
position: fixed;
|
||||
right: calc(var(--ha-space-4) + var(--safe-area-inset-right));
|
||||
bottom: calc(var(--ha-space-4) + var(--safe-area-inset-bottom));
|
||||
inset-inline-end: calc(var(--ha-space-4) + var(--safe-area-inset-right));
|
||||
inset-inline-start: initial;
|
||||
z-index: 1;
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import type { RouterOptions } from "../../../layouts/hass-router-page";
|
||||
import { HassRouterPage } from "../../../layouts/hass-router-page";
|
||||
import type { HomeAssistant, Route } from "../../../types";
|
||||
@@ -15,6 +16,12 @@ class HaConfigApps extends HassRouterPage {
|
||||
|
||||
protected routerOptions: RouterOptions = {
|
||||
defaultPage: "installed",
|
||||
beforeRender: () => {
|
||||
if (!isComponentLoaded(this.hass.config, "hassio")) {
|
||||
return "info";
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
routes: {
|
||||
installed: {
|
||||
tag: "ha-config-apps-installed",
|
||||
@@ -32,6 +39,10 @@ class HaConfigApps extends HassRouterPage {
|
||||
tag: "ha-config-apps-registries",
|
||||
load: () => import("./ha-config-apps-registries"),
|
||||
},
|
||||
info: {
|
||||
tag: "ha-config-apps-info",
|
||||
load: () => import("./ha-config-apps-info"),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
updateAreaRegistryEntry,
|
||||
} from "../../../data/area/area_registry";
|
||||
import type { AutomationEntity } from "../../../data/automation";
|
||||
import { fullEntitiesContext } from "../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../data/context/context";
|
||||
import type { DeviceRegistryEntry } from "../../../data/device/device_registry";
|
||||
import { sortDeviceRegistryByName } from "../../../data/device/device_registry";
|
||||
import type { EntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
||||
import "@home-assistant/webawesome/dist/components/popover/popover";
|
||||
import type WaPopover from "@home-assistant/webawesome/dist/components/popover/popover";
|
||||
import {
|
||||
mdiDelete,
|
||||
mdiDotsVertical,
|
||||
@@ -15,7 +17,7 @@ import {
|
||||
type PropertyValues,
|
||||
type TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import {
|
||||
@@ -24,9 +26,10 @@ import {
|
||||
type AreasFloorHierarchy,
|
||||
} from "../../../common/areas/areas-floor-hierarchy";
|
||||
import { formatListWithAnds } from "../../../common/string/format-list";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dropdown";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-floor-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-sortable";
|
||||
@@ -58,7 +61,6 @@ import {
|
||||
} from "./show-dialog-area-registry-detail";
|
||||
import { showAreasFloorsOrderDialog } from "./show-dialog-areas-floors-order";
|
||||
import { showFloorRegistryDetailDialog } from "./show-dialog-floor-registry-detail";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
|
||||
const UNASSIGNED_FLOOR = "__unassigned__";
|
||||
|
||||
@@ -84,10 +86,12 @@ export class HaConfigAreasDashboard extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
private _searchParms = new URLSearchParams(window.location.search);
|
||||
|
||||
@state() private _hierarchy?: AreasFloorHierarchy;
|
||||
|
||||
@query("wa-popover") private _popover?: WaPopover;
|
||||
|
||||
private _searchParms = new URLSearchParams(window.location.search);
|
||||
|
||||
private _blockHierarchyUpdate = false;
|
||||
|
||||
private _blockHierarchyUpdateTimeout?: number;
|
||||
@@ -318,27 +322,26 @@ export class HaConfigAreasDashboard extends LitElement {
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
class="floor"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.areas.picker.create_floor"
|
||||
)}
|
||||
extended
|
||||
@click=${this._createFloor}
|
||||
<ha-button id="fab" slot="fab" size="large">
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.hass.localize("ui.common.add")}
|
||||
</ha-button>
|
||||
<wa-popover
|
||||
trap-focus
|
||||
placement="top-start"
|
||||
distance="8"
|
||||
without-arrow
|
||||
for="fab"
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.areas.picker.create_area"
|
||||
)}
|
||||
extended
|
||||
@click=${this._createArea}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<ha-button appearance="filled" @click=${this._createFloor}>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.areas.picker.create_floor")}
|
||||
</ha-button>
|
||||
<ha-button appearance="filled" @click=${this._createArea}>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.areas.picker.create_area")}
|
||||
</ha-button>
|
||||
</wa-popover>
|
||||
</hass-tabs-subpage>
|
||||
`;
|
||||
}
|
||||
@@ -559,6 +562,7 @@ export class HaConfigAreasDashboard extends LitElement {
|
||||
}
|
||||
|
||||
private _createFloor() {
|
||||
this._popover?.hide();
|
||||
this._openFloorDialog();
|
||||
}
|
||||
|
||||
@@ -584,6 +588,7 @@ export class HaConfigAreasDashboard extends LitElement {
|
||||
}
|
||||
|
||||
private _createArea() {
|
||||
this._popover?.hide();
|
||||
this._openAreaDialog();
|
||||
}
|
||||
|
||||
@@ -725,6 +730,14 @@ export class HaConfigAreasDashboard extends LitElement {
|
||||
align-items: center;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
wa-popover::part(body) {
|
||||
gap: var(--ha-space-2);
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ import { storage } from "../../../../common/decorators/storage";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { preventDefaultStopPropagation } from "../../../../common/dom/prevent_default_stop_propagation";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import { computeObjectId } from "../../../../common/entity/compute_object_id";
|
||||
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
|
||||
import { handleStructError } from "../../../../common/structs/handle-errors";
|
||||
import { copyToClipboard } from "../../../../common/util/copy-clipboard";
|
||||
@@ -57,7 +59,7 @@ import type {
|
||||
} from "../../../../data/automation";
|
||||
import { CONDITION_BUILDING_BLOCKS } from "../../../../data/condition";
|
||||
import { validateConfig } from "../../../../data/config";
|
||||
import { fullEntitiesContext } from "../../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../../data/context/context";
|
||||
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import type {
|
||||
Action,
|
||||
@@ -95,8 +97,6 @@ import "./types/ha-automation-action-set_conversation_response";
|
||||
import "./types/ha-automation-action-stop";
|
||||
import "./types/ha-automation-action-wait_for_trigger";
|
||||
import "./types/ha-automation-action-wait_template";
|
||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||
import { computeObjectId } from "../../../../common/entity/compute_object_id";
|
||||
|
||||
export const getAutomationActionType = memoizeOne(
|
||||
(action: Action | undefined) => {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/device/ha-device-action-picker";
|
||||
import "../../../../../components/device/ha-device-picker";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { fullEntitiesContext } from "../../../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../../../data/context/context";
|
||||
import type {
|
||||
DeviceAction,
|
||||
DeviceCapabilities,
|
||||
|
||||
@@ -6,7 +6,7 @@ import "../../../../../components/ha-formfield";
|
||||
import "../../../../../components/ha-switch";
|
||||
import "../../../../../components/input/ha-input";
|
||||
import type { HaInput } from "../../../../../components/input/ha-input";
|
||||
import { localizeContext } from "../../../../../data/context";
|
||||
import { internationalizationContext } from "../../../../../data/context/context";
|
||||
import type { StopAction } from "../../../../../data/script";
|
||||
import type { ActionElement } from "../ha-automation-action-row";
|
||||
|
||||
@@ -17,8 +17,8 @@ export class HaStopAction extends LitElement implements ActionElement {
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: ContextType<typeof localizeContext>;
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
public static get defaultConfig(): StopAction {
|
||||
return { stop: "" };
|
||||
@@ -29,7 +29,7 @@ export class HaStopAction extends LitElement implements ActionElement {
|
||||
|
||||
return html`
|
||||
<ha-input
|
||||
.label=${this.localize(
|
||||
.label=${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.stop.stop"
|
||||
)}
|
||||
.value=${stop}
|
||||
@@ -37,7 +37,7 @@ export class HaStopAction extends LitElement implements ActionElement {
|
||||
@change=${this._stopChanged}
|
||||
></ha-input>
|
||||
<ha-input
|
||||
.label=${this.localize(
|
||||
.label=${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.stop.response_variable"
|
||||
)}
|
||||
.value=${response_variable || ""}
|
||||
@@ -46,7 +46,7 @@ export class HaStopAction extends LitElement implements ActionElement {
|
||||
></ha-input>
|
||||
<ha-formfield
|
||||
.disabled=${this.disabled}
|
||||
.label=${this.localize(
|
||||
.label=${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.actions.type.stop.error"
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -85,7 +85,7 @@ import {
|
||||
getConfigEntries,
|
||||
type ConfigEntry,
|
||||
} from "../../../data/config_entries";
|
||||
import { labelsContext } from "../../../data/context";
|
||||
import { labelsContext } from "../../../data/context/context";
|
||||
import { getDeviceEntityLookup } from "../../../data/device/device_registry";
|
||||
import type { EntityComboBoxItem } from "../../../data/entity/entity_picker";
|
||||
import { getFloorAreaLookup } from "../../../data/floor_registry";
|
||||
@@ -99,9 +99,9 @@ import {
|
||||
domainToName,
|
||||
fetchIntegrationManifests,
|
||||
} from "../../../data/integration";
|
||||
import { filterSelectorEntities } from "../../../data/selector";
|
||||
import type { LabelRegistryEntry } from "../../../data/label/label_registry";
|
||||
import { subscribeLabFeature } from "../../../data/labs";
|
||||
import { filterSelectorEntities } from "../../../data/selector";
|
||||
import {
|
||||
TARGET_SEPARATOR,
|
||||
getConditionsForTarget,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import "@home-assistant/webawesome/dist/components/tree-item/tree-item";
|
||||
import "@home-assistant/webawesome/dist/components/tree/tree";
|
||||
import type { WaSelectionChangeEvent } from "@home-assistant/webawesome/dist/events/selection-change";
|
||||
import { consume } from "@lit/context";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import { mdiTextureBox } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import {
|
||||
@@ -44,14 +44,11 @@ import {
|
||||
type ConfigEntry,
|
||||
} from "../../../../data/config_entries";
|
||||
import {
|
||||
areasContext,
|
||||
devicesContext,
|
||||
entitiesContext,
|
||||
floorsContext,
|
||||
internationalizationContext,
|
||||
labelsContext,
|
||||
localizeContext,
|
||||
registriesContext,
|
||||
statesContext,
|
||||
} from "../../../../data/context";
|
||||
} from "../../../../data/context/context";
|
||||
import { getDeviceEntityLookup } from "../../../../data/device/device_registry";
|
||||
import {
|
||||
domainToName,
|
||||
@@ -99,28 +96,14 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
|
||||
// #region context
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: HomeAssistant["localize"];
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: statesContext, subscribe: true })
|
||||
private states!: HomeAssistant["states"];
|
||||
private states!: ContextType<typeof statesContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: floorsContext, subscribe: true })
|
||||
private floors!: HomeAssistant["floors"];
|
||||
|
||||
@state()
|
||||
@consume({ context: areasContext, subscribe: true })
|
||||
private areas!: HomeAssistant["areas"];
|
||||
|
||||
@state()
|
||||
@consume({ context: devicesContext, subscribe: true })
|
||||
private devices!: HomeAssistant["devices"];
|
||||
|
||||
@state()
|
||||
@consume({ context: entitiesContext, subscribe: true })
|
||||
private entities!: HomeAssistant["entities"];
|
||||
@consume({ context: registriesContext, subscribe: true })
|
||||
private _registries!: ContextType<typeof registriesContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: labelsContext, subscribe: true })
|
||||
@@ -205,7 +188,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
? html`
|
||||
<div class="targets-show-more">
|
||||
<ha-button appearance="filled" @click=${this._expandHeight}>
|
||||
${this.localize("ui.panel.config.automation.editor.show_more")}
|
||||
${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.show_more"
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
`
|
||||
@@ -235,7 +220,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
if (valueType === "area" && valueId) {
|
||||
const floor =
|
||||
entries[
|
||||
`floor${TARGET_SEPARATOR}${this.areas[valueId]?.floor_id || ""}`
|
||||
`floor${TARGET_SEPARATOR}${this._registries.areas[valueId]?.floor_id || ""}`
|
||||
];
|
||||
const { devices, entities } =
|
||||
floor.areas![`area${TARGET_SEPARATOR}${valueId}`];
|
||||
@@ -255,9 +240,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
if (valueId && valueType === "device") {
|
||||
const areaId = this.devices[valueId]?.area_id;
|
||||
const areaId = this._registries.devices[valueId]?.area_id;
|
||||
if (areaId) {
|
||||
const floorId = this.areas[areaId]?.floor_id || "";
|
||||
const floorId = this._registries.areas[areaId]?.floor_id || "";
|
||||
const { entities } =
|
||||
entries[`floor${TARGET_SEPARATOR}${floorId}`].areas![
|
||||
`area${TARGET_SEPARATOR}${areaId}`
|
||||
@@ -266,7 +251,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
return entities.length ? this._renderEntities(entities) : nothing;
|
||||
}
|
||||
|
||||
const device = this.devices[valueId];
|
||||
const device = this._registries.devices[valueId];
|
||||
const isService = device.entry_type === "service";
|
||||
const { entities } =
|
||||
entries[`${isService ? "service" : "area"}${TARGET_SEPARATOR}`]
|
||||
@@ -316,7 +301,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
)
|
||||
: this._renderItem(
|
||||
!floor.id
|
||||
? this.localize(
|
||||
? this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.other_areas"
|
||||
)
|
||||
: floor.primary,
|
||||
@@ -337,7 +322,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
)
|
||||
);
|
||||
return html`<ha-section-title
|
||||
>${this.localize(
|
||||
>${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.home"
|
||||
)}</ha-section-title
|
||||
>
|
||||
@@ -345,7 +330,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
? html`<ha-md-list>
|
||||
<ha-md-list-item type="text">
|
||||
<div slot="headline">
|
||||
${this.localize("ui.components.area-picker.no_areas")}
|
||||
${this._i18n.localize("ui.components.area-picker.no_areas")}
|
||||
</div>
|
||||
</ha-md-list-item>
|
||||
</ha-md-list>`
|
||||
@@ -362,9 +347,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
(narrow: boolean, value?: SingleHassServiceTarget) => {
|
||||
const labels = this._getLabelsMemoized(
|
||||
this.states,
|
||||
this.areas,
|
||||
this.devices,
|
||||
this.entities,
|
||||
this._registries.areas,
|
||||
this._registries.devices,
|
||||
this._registries.entities,
|
||||
this._labelRegistry,
|
||||
undefined,
|
||||
undefined,
|
||||
@@ -380,7 +365,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
return html`<ha-section-title
|
||||
>${this.localize(
|
||||
>${this._i18n.localize(
|
||||
"ui.components.label-picker.labels"
|
||||
)}</ha-section-title
|
||||
>
|
||||
@@ -447,7 +432,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
const open = entries[`device${TARGET_SEPARATOR}`].open;
|
||||
items.push(
|
||||
this._renderItem(
|
||||
this.localize("ui.components.target-picker.type.entities"),
|
||||
this._i18n.localize("ui.components.target-picker.type.entities"),
|
||||
`device${TARGET_SEPARATOR}`,
|
||||
true,
|
||||
false,
|
||||
@@ -468,7 +453,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
const open = entries[`helper${TARGET_SEPARATOR}`].open;
|
||||
items.push(
|
||||
this._renderItem(
|
||||
this.localize("ui.panel.config.automation.editor.helpers"),
|
||||
this._i18n.localize("ui.panel.config.automation.editor.helpers"),
|
||||
`helper${TARGET_SEPARATOR}`,
|
||||
true,
|
||||
false,
|
||||
@@ -489,7 +474,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
const open = entries[`area${TARGET_SEPARATOR}`].open;
|
||||
items.push(
|
||||
this._renderItem(
|
||||
this.localize("ui.components.target-picker.type.devices"),
|
||||
this._i18n.localize("ui.components.target-picker.type.devices"),
|
||||
`area${TARGET_SEPARATOR}`,
|
||||
true,
|
||||
false,
|
||||
@@ -507,7 +492,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
const open = entries[`service${TARGET_SEPARATOR}`].open;
|
||||
items.push(
|
||||
this._renderItem(
|
||||
this.localize("ui.panel.config.automation.editor.services"),
|
||||
this._i18n.localize("ui.panel.config.automation.editor.services"),
|
||||
`service${TARGET_SEPARATOR}`,
|
||||
true,
|
||||
false,
|
||||
@@ -524,7 +509,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
return html`<ha-section-title
|
||||
>${this.localize(
|
||||
>${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.unassigned"
|
||||
)}</ha-section-title
|
||||
>${narrow
|
||||
@@ -539,11 +524,11 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
const renderedAreas = Object.keys(areas)
|
||||
.filter((areaTargetId) => {
|
||||
const [, areaId] = areaTargetId.split(TARGET_SEPARATOR, 2);
|
||||
return this.areas[areaId];
|
||||
return this._registries.areas[areaId];
|
||||
})
|
||||
.map((areaTargetId) => {
|
||||
const [, areaId] = areaTargetId.split(TARGET_SEPARATOR, 2);
|
||||
const area = this.areas[areaId];
|
||||
const area = this._registries.areas[areaId];
|
||||
return [
|
||||
areaTargetId,
|
||||
computeAreaName(area) || area.area_id,
|
||||
@@ -578,7 +563,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
|
||||
if (this.narrow) {
|
||||
return html`<ha-section-title
|
||||
>${this.localize(
|
||||
>${this._i18n.localize(
|
||||
"ui.components.target-picker.type.areas"
|
||||
)}</ha-section-title
|
||||
>
|
||||
@@ -590,9 +575,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
|
||||
private _renderDevices(devices: Record<string, Level3Entries>) {
|
||||
const renderedDevices = Object.keys(devices)
|
||||
.filter((deviceId) => this.devices[deviceId])
|
||||
.filter((deviceId) => this._registries.devices[deviceId])
|
||||
.map((deviceId) => {
|
||||
const device = this.devices[deviceId];
|
||||
const device = this._registries.devices[deviceId];
|
||||
const configEntry = device.primary_config_entry
|
||||
? this._configEntryLookup?.[device.primary_config_entry]
|
||||
: undefined;
|
||||
@@ -627,7 +612,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
|
||||
if (this.narrow) {
|
||||
return html`<ha-section-title
|
||||
>${this.localize(
|
||||
>${this._i18n.localize(
|
||||
"ui.components.target-picker.type.devices"
|
||||
)}</ha-section-title
|
||||
>
|
||||
@@ -648,7 +633,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
domainTargetId.length - TARGET_SEPARATOR.length
|
||||
);
|
||||
const label = domainToName(
|
||||
this.localize,
|
||||
this._i18n.localize,
|
||||
domain,
|
||||
this.manifests![domain]
|
||||
);
|
||||
@@ -674,7 +659,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
|
||||
if (this.narrow) {
|
||||
return html`<ha-section-title
|
||||
>${this.localize(
|
||||
>${this._i18n.localize(
|
||||
"ui.components.target-picker.type.devices"
|
||||
)}</ha-section-title
|
||||
>
|
||||
@@ -697,16 +682,16 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
const [entityName, deviceName] = computeEntityNameList(
|
||||
stateObj,
|
||||
[{ type: "entity" }, { type: "device" }, { type: "area" }],
|
||||
this.entities,
|
||||
this.devices,
|
||||
this.areas,
|
||||
this.floors
|
||||
this._registries.entities,
|
||||
this._registries.devices,
|
||||
this._registries.areas,
|
||||
this._registries.floors
|
||||
);
|
||||
|
||||
let label = entityName || deviceName || entityId;
|
||||
|
||||
if (this.entities[entityId]?.hidden) {
|
||||
label += ` (${this.localize("ui.panel.config.automation.editor.entity_hidden")})`;
|
||||
if (this._registries.entities[entityId]?.hidden) {
|
||||
label += ` (${this._i18n.localize("ui.panel.config.automation.editor.entity_hidden")})`;
|
||||
}
|
||||
|
||||
return [entityId, label, stateObj] as [string, string, HassEntity];
|
||||
@@ -729,7 +714,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
|
||||
if (this.narrow) {
|
||||
return html`<ha-section-title
|
||||
>${this.localize(
|
||||
>${this._i18n.localize(
|
||||
"ui.components.target-picker.type.entities"
|
||||
)}</ha-section-title
|
||||
>
|
||||
@@ -867,10 +852,10 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
private _getTreeData() {
|
||||
this._floorAreas = getAreasNestedInFloors(
|
||||
this.states,
|
||||
this.floors,
|
||||
this.areas,
|
||||
this.devices,
|
||||
this.entities,
|
||||
this._registries.floors,
|
||||
this._registries.areas,
|
||||
this._registries.devices,
|
||||
this._registries.entities,
|
||||
this._formatId,
|
||||
undefined,
|
||||
undefined,
|
||||
@@ -903,7 +888,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
private _loadUnassignedDevices() {
|
||||
const unassignedDevices = Object.values(this.devices).filter(
|
||||
const unassignedDevices = Object.values(this._registries.devices).filter(
|
||||
(device) => !device.area_id
|
||||
);
|
||||
|
||||
@@ -912,16 +897,16 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
const services: Record<string, Level3Entries> = {};
|
||||
|
||||
unassignedDevices.forEach(({ id: deviceId, entry_type }) => {
|
||||
const device = this.devices[deviceId];
|
||||
const device = this._registries.devices[deviceId];
|
||||
if (!device || device.disabled_by) {
|
||||
return;
|
||||
}
|
||||
const deviceEntry = {
|
||||
open: false,
|
||||
entities:
|
||||
this._getDeviceEntityLookupMemoized(this.entities)[deviceId]?.map(
|
||||
(entity) => entity.entity_id
|
||||
) || [],
|
||||
this._getDeviceEntityLookupMemoized(this._registries.entities)[
|
||||
deviceId
|
||||
]?.map((entity) => entity.entity_id) || [],
|
||||
};
|
||||
if (entry_type === "service") {
|
||||
services[deviceId] = deviceEntry;
|
||||
@@ -953,7 +938,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
private _loadUnassignedEntities() {
|
||||
Object.values(this.entities)
|
||||
Object.values(this._registries.entities)
|
||||
.filter((entity) => !entity.area_id && !entity.device_id)
|
||||
.forEach(({ entity_id }) => {
|
||||
const domain = entity_id.split(".", 2)[0];
|
||||
@@ -1010,23 +995,23 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
private _loadArea(area: FloorComboBoxItem) {
|
||||
const [, id] = area.id.split(TARGET_SEPARATOR, 2);
|
||||
const referenced_devices =
|
||||
this._getAreaDeviceLookupMemoized(this.devices)[id] || [];
|
||||
this._getAreaDeviceLookupMemoized(this._registries.devices)[id] || [];
|
||||
const referenced_entities =
|
||||
this._getAreaEntityLookupMemoized(this.entities)[id] || [];
|
||||
this._getAreaEntityLookupMemoized(this._registries.entities)[id] || [];
|
||||
|
||||
const devices: Record<string, Level3Entries> = {};
|
||||
|
||||
referenced_devices.forEach(({ id: deviceId }) => {
|
||||
const device = this.devices[deviceId];
|
||||
const device = this._registries.devices[deviceId];
|
||||
if (!device || device.disabled_by) {
|
||||
return;
|
||||
}
|
||||
devices[deviceId] = {
|
||||
open: false,
|
||||
entities:
|
||||
this._getDeviceEntityLookupMemoized(this.entities)[deviceId]?.map(
|
||||
(entity) => entity.entity_id
|
||||
) || [],
|
||||
this._getDeviceEntityLookupMemoized(this._registries.entities)[
|
||||
deviceId
|
||||
]?.map((entity) => entity.entity_id) || [],
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1051,17 +1036,17 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
if (type === "entity") {
|
||||
const deviceId = this.entities[id]?.device_id;
|
||||
const device = deviceId ? this.devices[deviceId] : undefined;
|
||||
const deviceId = this._registries.entities[id]?.device_id;
|
||||
const device = deviceId ? this._registries.devices[deviceId] : undefined;
|
||||
const deviceAreaId = (deviceId && device?.area_id) || undefined;
|
||||
|
||||
if (!deviceAreaId) {
|
||||
let floor: string;
|
||||
let area: string;
|
||||
const entity = this.entities[id];
|
||||
const entity = this._registries.entities[id];
|
||||
|
||||
if (!deviceId && entity.area_id) {
|
||||
floor = `floor${TARGET_SEPARATOR}${this.areas[entity.area_id]?.floor_id || ""}`;
|
||||
floor = `floor${TARGET_SEPARATOR}${this._registries.areas[entity.area_id]?.floor_id || ""}`;
|
||||
area = `area${TARGET_SEPARATOR}${entity.area_id}`;
|
||||
} else if (!deviceId) {
|
||||
const domain = id.split(".", 1)[0];
|
||||
@@ -1093,7 +1078,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const floor = `floor${TARGET_SEPARATOR}${this.areas[deviceAreaId]?.floor_id || ""}`;
|
||||
const floor = `floor${TARGET_SEPARATOR}${this._registries.areas[deviceAreaId]?.floor_id || ""}`;
|
||||
const area = `area${TARGET_SEPARATOR}${deviceAreaId}`;
|
||||
|
||||
this._entries = {
|
||||
@@ -1121,10 +1106,10 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
if (type === "device") {
|
||||
const deviceAreaId = this.devices[id]?.area_id;
|
||||
const deviceAreaId = this._registries.devices[id]?.area_id;
|
||||
|
||||
if (!deviceAreaId) {
|
||||
const device = this.devices[id];
|
||||
const device = this._registries.devices[id];
|
||||
const floor = `${device.entry_type === "service" ? "service" : "area"}${TARGET_SEPARATOR}`;
|
||||
this._entries = {
|
||||
...this._entries,
|
||||
@@ -1136,7 +1121,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
return;
|
||||
}
|
||||
|
||||
const floor = `floor${TARGET_SEPARATOR}${this.areas[deviceAreaId]?.floor_id || ""}`;
|
||||
const floor = `floor${TARGET_SEPARATOR}${this._registries.areas[deviceAreaId]?.floor_id || ""}`;
|
||||
const area = `area${TARGET_SEPARATOR}${deviceAreaId}`;
|
||||
|
||||
this._entries = {
|
||||
@@ -1157,7 +1142,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
if (type === "area") {
|
||||
const floor = `floor${TARGET_SEPARATOR}${this.areas[id]?.floor_id || ""}`;
|
||||
const floor = `floor${TARGET_SEPARATOR}${this._registries.areas[id]?.floor_id || ""}`;
|
||||
this._entries = {
|
||||
...this._entries,
|
||||
[floor]: {
|
||||
@@ -1231,7 +1216,7 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
if (type === "area" && id) {
|
||||
const floorId = `floor${TARGET_SEPARATOR}${this.areas[id]?.floor_id || ""}`;
|
||||
const floorId = `floor${TARGET_SEPARATOR}${this._registries.areas[id]?.floor_id || ""}`;
|
||||
|
||||
this._entries = {
|
||||
...this._entries,
|
||||
@@ -1272,10 +1257,10 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
if (type === "device" && id) {
|
||||
const areaId = this.devices[id]?.area_id;
|
||||
const areaId = this._registries.devices[id]?.area_id;
|
||||
if (areaId) {
|
||||
const areaTargetId = `area${TARGET_SEPARATOR}${this.devices[id]?.area_id ?? ""}`;
|
||||
const floorId = `floor${TARGET_SEPARATOR}${(areaId && this.areas[areaId]?.floor_id) || ""}`;
|
||||
const areaTargetId = `area${TARGET_SEPARATOR}${this._registries.devices[id]?.area_id ?? ""}`;
|
||||
const floorId = `floor${TARGET_SEPARATOR}${(areaId && this._registries.areas[areaId]?.floor_id) || ""}`;
|
||||
|
||||
this._entries = {
|
||||
...this._entries,
|
||||
@@ -1300,7 +1285,9 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
||||
}
|
||||
|
||||
const deviceType =
|
||||
this.devices[id]?.entry_type === "service" ? "service" : "area";
|
||||
this._registries.devices[id]?.entry_type === "service"
|
||||
? "service"
|
||||
: "area";
|
||||
const floorId = `${deviceType}${TARGET_SEPARATOR}`;
|
||||
this._entries = {
|
||||
...this._entries,
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
} from "../../../../data/area_floor_picker";
|
||||
import { CONDITION_BUILDING_BLOCKS_GROUP } from "../../../../data/condition";
|
||||
import type { ConfigEntry } from "../../../../data/config_entries";
|
||||
import { labelsContext } from "../../../../data/context";
|
||||
import { labelsContext } from "../../../../data/context/context";
|
||||
import {
|
||||
deviceComboBoxKeys,
|
||||
getDevices,
|
||||
@@ -450,8 +450,10 @@ export class HaAutomationAddSearch extends LitElement {
|
||||
private _keyFunction = (item: PickerComboBoxItem | string) =>
|
||||
typeof item === "string" ? item : item.id;
|
||||
|
||||
private _createFuseIndex = (states, keys: FuseWeightedKey[]) =>
|
||||
Fuse.createIndex(keys, states);
|
||||
private _createFuseIndex = (
|
||||
states: PickerComboBoxItem[],
|
||||
keys: FuseWeightedKey[]
|
||||
) => Fuse.createIndex(keys, states);
|
||||
|
||||
private _fuseIndexes = {
|
||||
area: memoizeOne((states: PickerComboBoxItem[]) =>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-markdown";
|
||||
import "../../../components/ha-fab";
|
||||
import type { BlueprintAutomationConfig } from "../../../data/automation";
|
||||
import { fetchBlueprints } from "../../../data/blueprint";
|
||||
import { HaBlueprintGenericEditor } from "../blueprint/blueprint-generic-editor";
|
||||
@@ -56,16 +55,16 @@ export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
|
||||
: nothing}
|
||||
${this.renderCard()}
|
||||
|
||||
<ha-fab
|
||||
<ha-button
|
||||
slot="fab"
|
||||
size="large"
|
||||
class=${this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this._saveAutomation}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<ha-svg-icon slot="start" .path=${mdiContentSave}></ha-svg-icon>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -109,8 +108,9 @@ export class HaBlueprintAutomationEditor extends HaBlueprintGenericEditor {
|
||||
)
|
||||
);
|
||||
}
|
||||
ha-fab {
|
||||
ha-button[slot="fab"] {
|
||||
position: fixed;
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -36,6 +36,7 @@ import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-condition-icon";
|
||||
import "../../../../components/ha-dropdown";
|
||||
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
import "../../../../components/ha-dropdown-item";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
@@ -50,7 +51,7 @@ import { describeCondition } from "../../../../data/automation_i18n";
|
||||
import type { ConditionDescriptions } from "../../../../data/condition";
|
||||
import { CONDITION_BUILDING_BLOCKS } from "../../../../data/condition";
|
||||
import { validateConfig } from "../../../../data/config";
|
||||
import { fullEntitiesContext } from "../../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../../data/context/context";
|
||||
import type { DeviceCondition } from "../../../../data/device/device_automation";
|
||||
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import {
|
||||
@@ -76,7 +77,6 @@ import "./types/ha-automation-condition-template";
|
||||
import "./types/ha-automation-condition-time";
|
||||
import "./types/ha-automation-condition-trigger";
|
||||
import "./types/ha-automation-condition-zone";
|
||||
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
|
||||
export interface ConditionElement extends LitElement {
|
||||
condition: Condition;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/device/ha-device-condition-picker";
|
||||
import "../../../../../components/device/ha-device-picker";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { fullEntitiesContext } from "../../../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../../../data/context/context";
|
||||
import type {
|
||||
DeviceCapabilities,
|
||||
DeviceCondition,
|
||||
|
||||
@@ -32,7 +32,6 @@ import { promiseTimeout } from "../../../common/util/promise-timeout";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-svg-icon";
|
||||
@@ -148,7 +147,7 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
this._newAutomationId &&
|
||||
changedProps.has("entityRegistry")
|
||||
) {
|
||||
const automation = this.entityRegistry.find(
|
||||
const automation = this.entityRegistry?.find(
|
||||
(entity: EntityRegistryEntry) =>
|
||||
entity.platform === "automation" &&
|
||||
entity.unique_id === this._newAutomationId
|
||||
@@ -547,19 +546,19 @@ export class HaAutomationEditor extends AutomationScriptEditorMixin<AutomationCo
|
||||
.showErrors=${false}
|
||||
disable-fullscreen
|
||||
></ha-yaml-editor>
|
||||
<ha-fab
|
||||
<ha-button
|
||||
slot="fab"
|
||||
size="large"
|
||||
class=${this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this._handleSaveAutomation}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
slot="start"
|
||||
.path=${mdiContentSave}
|
||||
></ha-svg-icon>
|
||||
</ha-fab>`
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>`
|
||||
: nothing}
|
||||
</div>
|
||||
</hass-subpage>
|
||||
|
||||
@@ -45,13 +45,13 @@ import type {
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/data-table/ha-data-table-labels";
|
||||
import "../../../components/entity/ha-entity-toggle";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dropdown";
|
||||
import type {
|
||||
HaDropdown,
|
||||
HaDropdownSelectEvent,
|
||||
} from "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-filter-blueprints";
|
||||
import "../../../components/ha-filter-categories";
|
||||
import "../../../components/ha-filter-devices";
|
||||
@@ -79,7 +79,10 @@ import {
|
||||
subscribeCategoryRegistry,
|
||||
} from "../../../data/category_registry";
|
||||
import type { CloudStatus } from "../../../data/cloud";
|
||||
import { fullEntitiesContext, labelsContext } from "../../../data/context";
|
||||
import {
|
||||
fullEntitiesContext,
|
||||
labelsContext,
|
||||
} from "../../../data/context/context";
|
||||
import type { DataTableFilters } from "../../../data/data_table_filters";
|
||||
import {
|
||||
deserializeFilters,
|
||||
@@ -691,16 +694,12 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
||||
</ha-button>
|
||||
</div>`
|
||||
: nothing}
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
.label=${this.hass.localize(
|
||||
<ha-button slot="fab" size="large" @click=${this._createNew}>
|
||||
<ha-svg-icon slot="start" .path=${mdiPlus}></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.picker.add_automation"
|
||||
)}
|
||||
extended
|
||||
@click=${this._createNew}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
</ha-button>
|
||||
</hass-tabs-subpage-data-table>
|
||||
<ha-dropdown
|
||||
id="overflow-menu"
|
||||
|
||||
@@ -7,7 +7,7 @@ import { goBack, navigate } from "../../../common/navigate";
|
||||
import { afterNextRender } from "../../../common/util/render-status";
|
||||
import "../../../components/ha-fade-in";
|
||||
import "../../../components/ha-spinner"; // used by renderLoading() provided to both editors
|
||||
import { fullEntitiesContext } from "../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../data/context/context";
|
||||
import type { EntityRegistryEntry } from "../../../data/entity/entity_registry";
|
||||
import {
|
||||
showAlertDialog,
|
||||
@@ -50,13 +50,14 @@ export const automationScriptEditorStyles: CSSResult = css`
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ha-fab {
|
||||
ha-button[slot="fab"] {
|
||||
position: fixed;
|
||||
right: calc(16px + var(--safe-area-inset-right, 0px));
|
||||
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
ha-fab.dirty {
|
||||
ha-button[slot="fab"].dirty {
|
||||
bottom: calc(16px + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
ha-tooltip ha-svg-icon {
|
||||
@@ -93,7 +94,7 @@ export const AutomationScriptEditorMixin = <TConfig extends BaseEditorConfig>(
|
||||
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
entityRegistry!: EntityRegistryEntry[];
|
||||
entityRegistry?: EntityRegistryEntry[];
|
||||
|
||||
@state() protected dirty = false;
|
||||
|
||||
@@ -234,7 +235,7 @@ export const AutomationScriptEditorMixin = <TConfig extends BaseEditorConfig>(
|
||||
goBack("/config");
|
||||
return;
|
||||
}
|
||||
const entity = this.entityRegistry.find(
|
||||
const entity = this.entityRegistry?.find(
|
||||
(ent) => ent.platform === domain && ent.unique_id === id
|
||||
);
|
||||
if (entity) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
extractSearchParam,
|
||||
removeSearchParam,
|
||||
} from "../../../common/url/search-params";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-markdown";
|
||||
import type { SidebarConfig } from "../../../data/automation";
|
||||
@@ -112,16 +112,16 @@ export const ManualEditorMixin = <TConfig>(
|
||||
${this.renderContent()}
|
||||
</div>
|
||||
<div class="fab-positioner">
|
||||
<ha-fab
|
||||
<ha-button
|
||||
slot="fab"
|
||||
size="large"
|
||||
class=${this.dirty ? "dirty" : ""}
|
||||
.label=${this.hass.localize("ui.common.save")}
|
||||
.disabled=${this.saving}
|
||||
extended
|
||||
@click=${this.saveConfig}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiContentSave}></ha-svg-icon>
|
||||
</ha-fab>
|
||||
<ha-svg-icon slot="start" .path=${mdiContentSave}></ha-svg-icon>
|
||||
${this.hass.localize("ui.common.save")}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-positioner">
|
||||
|
||||
@@ -17,7 +17,6 @@ import { ensureArray } from "../../../common/array/ensure-array";
|
||||
import { canOverrideAlphanumericInput } from "../../../common/dom/can-override-input";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-markdown";
|
||||
import type {
|
||||
|
||||
@@ -21,6 +21,7 @@ import "../../../../components/ha-automation-row";
|
||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-dropdown";
|
||||
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
import "../../../../components/ha-dropdown-item";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
@@ -30,7 +31,7 @@ import type {
|
||||
OptionSidebarConfig,
|
||||
} from "../../../../data/automation";
|
||||
import { describeCondition } from "../../../../data/automation_i18n";
|
||||
import { fullEntitiesContext } from "../../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../../data/context/context";
|
||||
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import type { Action, Option } from "../../../../data/script";
|
||||
import { showPromptDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||
@@ -47,7 +48,6 @@ import {
|
||||
overflowStyles,
|
||||
rowStyles,
|
||||
} from "../styles";
|
||||
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
|
||||
@customElement("ha-automation-option-row")
|
||||
export default class HaAutomationOptionRow extends LitElement {
|
||||
|
||||
@@ -96,13 +96,14 @@ export const saveFabStyles = css`
|
||||
:host {
|
||||
overflow: hidden;
|
||||
}
|
||||
ha-fab {
|
||||
ha-button[slot="fab"] {
|
||||
position: absolute;
|
||||
right: calc(16px + var(--safe-area-inset-right, 0px));
|
||||
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
--ha-button-box-shadow: var(--ha-box-shadow-l);
|
||||
}
|
||||
ha-fab.dirty {
|
||||
ha-button[slot="fab"].dirty {
|
||||
bottom: calc(16px + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
`;
|
||||
@@ -129,14 +130,14 @@ export const manualEditorStyles = css`
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.fab-positioner ha-fab {
|
||||
.fab-positioner ha-button[slot="fab"] {
|
||||
position: fixed;
|
||||
right: unset;
|
||||
left: unset;
|
||||
bottom: calc(-80px - var(--safe-area-inset-bottom));
|
||||
transition: bottom 0.3s;
|
||||
}
|
||||
.fab-positioner ha-fab.dirty {
|
||||
.fab-positioner ha-button[slot="fab"].dirty {
|
||||
bottom: calc(16px + var(--safe-area-inset-bottom, 0px));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { consume } from "@lit/context";
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import {
|
||||
mdiAlert,
|
||||
mdiAlertOctagon,
|
||||
@@ -16,14 +16,11 @@ import { isTemplate } from "../../../../common/string/has-template";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import type { ConfigEntry } from "../../../../data/config_entries";
|
||||
import {
|
||||
areasContext,
|
||||
configEntriesContext,
|
||||
devicesContext,
|
||||
floorsContext,
|
||||
internationalizationContext,
|
||||
labelsContext,
|
||||
localizeContext,
|
||||
statesContext,
|
||||
} from "../../../../data/context";
|
||||
registriesContext,
|
||||
} from "../../../../data/context/context";
|
||||
import type { LabelRegistryEntry } from "../../../../data/label/label_registry";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import { getTargetIcon } from "./get_target_icon";
|
||||
@@ -41,24 +38,12 @@ export class HaAutomationRowTargets extends LitElement {
|
||||
public targetRequired = false;
|
||||
|
||||
@state()
|
||||
@consume({ context: localizeContext, subscribe: true })
|
||||
private localize!: HomeAssistant["localize"];
|
||||
@consume({ context: internationalizationContext, subscribe: true })
|
||||
private _i18n!: ContextType<typeof internationalizationContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: floorsContext, subscribe: true })
|
||||
private floors!: HomeAssistant["floors"];
|
||||
|
||||
@state()
|
||||
@consume({ context: areasContext, subscribe: true })
|
||||
private areas!: HomeAssistant["areas"];
|
||||
|
||||
@state()
|
||||
@consume({ context: devicesContext, subscribe: true })
|
||||
private devices!: HomeAssistant["devices"];
|
||||
|
||||
@state()
|
||||
@consume({ context: statesContext, subscribe: true })
|
||||
private states!: HomeAssistant["states"];
|
||||
@consume({ context: registriesContext, subscribe: true })
|
||||
private _registries!: ContextType<typeof registriesContext>;
|
||||
|
||||
@state()
|
||||
@consume({ context: labelsContext, subscribe: true })
|
||||
@@ -82,7 +67,7 @@ export class HaAutomationRowTargets extends LitElement {
|
||||
this.targetRequired
|
||||
? html`<ha-svg-icon .path=${mdiAlertOctagon}></ha-svg-icon>`
|
||||
: nothing,
|
||||
this.localize(
|
||||
this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.target_summary.no_target"
|
||||
),
|
||||
false,
|
||||
@@ -124,7 +109,7 @@ export class HaAutomationRowTargets extends LitElement {
|
||||
return html`<span class="target">
|
||||
<ha-svg-icon .path=${mdiFormatListBulleted}></ha-svg-icon>
|
||||
<div class="label">
|
||||
${this.localize(
|
||||
${this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.target_summary.targets",
|
||||
{
|
||||
count: totalLength,
|
||||
@@ -142,16 +127,16 @@ export class HaAutomationRowTargets extends LitElement {
|
||||
targetId: string
|
||||
): boolean {
|
||||
if (targetType === "floor") {
|
||||
return !!this.floors[targetId];
|
||||
return !!this._registries.floors[targetId];
|
||||
}
|
||||
if (targetType === "area") {
|
||||
return !!this.areas[targetId];
|
||||
return !!this._registries.areas[targetId];
|
||||
}
|
||||
if (targetType === "device") {
|
||||
return !!this.devices[targetId];
|
||||
return !!this._registries.devices[targetId];
|
||||
}
|
||||
if (targetType === "entity") {
|
||||
return !!this.states[targetId];
|
||||
return !!this._registries.entities[targetId];
|
||||
}
|
||||
if (targetType === "label") {
|
||||
return !!this._getLabel(targetId);
|
||||
@@ -178,7 +163,7 @@ export class HaAutomationRowTargets extends LitElement {
|
||||
if (targetType === "entity" && ["all", "none"].includes(targetId)) {
|
||||
return this._renderTargetBadge(
|
||||
html`<ha-svg-icon .path=${mdiShape}></ha-svg-icon>`,
|
||||
this.localize(
|
||||
this._i18n.localize(
|
||||
`ui.panel.config.automation.editor.target_summary.${targetId as "all" | "none"}_entities`
|
||||
)
|
||||
);
|
||||
@@ -188,7 +173,7 @@ export class HaAutomationRowTargets extends LitElement {
|
||||
if (isTemplate(targetId)) {
|
||||
return this._renderTargetBadge(
|
||||
html`<ha-svg-icon .path=${mdiCodeBraces}></ha-svg-icon>`,
|
||||
this.localize(
|
||||
this._i18n.localize(
|
||||
"ui.panel.config.automation.editor.target_summary.template"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -39,6 +39,7 @@ import "../../../../components/ha-automation-row";
|
||||
import type { HaAutomationRow } from "../../../../components/ha-automation-row";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-dropdown";
|
||||
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
import "../../../../components/ha-dropdown-item";
|
||||
import "../../../../components/ha-expansion-panel";
|
||||
import "../../../../components/ha-icon-button";
|
||||
@@ -54,7 +55,7 @@ import type {
|
||||
import { isTrigger, subscribeTrigger } from "../../../../data/automation";
|
||||
import { describeTrigger } from "../../../../data/automation_i18n";
|
||||
import { validateConfig } from "../../../../data/config";
|
||||
import { fullEntitiesContext } from "../../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../../data/context/context";
|
||||
import type { DeviceTrigger } from "../../../../data/device/device_automation";
|
||||
import type { EntityRegistryEntry } from "../../../../data/entity/entity_registry";
|
||||
import type { TriggerDescriptions } from "../../../../data/trigger";
|
||||
@@ -89,7 +90,6 @@ import "./types/ha-automation-trigger-time";
|
||||
import "./types/ha-automation-trigger-time_pattern";
|
||||
import "./types/ha-automation-trigger-webhook";
|
||||
import "./types/ha-automation-trigger-zone";
|
||||
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
|
||||
|
||||
export interface TriggerElement extends LitElement {
|
||||
trigger: Trigger;
|
||||
|
||||
@@ -9,7 +9,7 @@ import "../../../../../components/device/ha-device-picker";
|
||||
import "../../../../../components/device/ha-device-trigger-picker";
|
||||
import { computeInitialHaFormData } from "../../../../../components/ha-form/compute-initial-ha-form-data";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import { fullEntitiesContext } from "../../../../../data/context";
|
||||
import { fullEntitiesContext } from "../../../../../data/context/context";
|
||||
import type {
|
||||
DeviceCapabilities,
|
||||
DeviceTrigger,
|
||||
|
||||
@@ -31,7 +31,6 @@ import type {
|
||||
HaDropdownSelectEvent,
|
||||
} from "../../../components/ha-dropdown";
|
||||
import "../../../components/ha-dropdown-item";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-filter-states";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-icon-next";
|
||||
@@ -477,24 +476,24 @@ class HaConfigBackupBackups extends SubscribeMixin(LitElement) {
|
||||
></ha-filter-states>
|
||||
${!this._needsOnboarding
|
||||
? html`
|
||||
<ha-fab
|
||||
<ha-button
|
||||
slot="fab"
|
||||
size="large"
|
||||
?disabled=${backupInProgress}
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.backup.backups.new_backup"
|
||||
)}
|
||||
extended
|
||||
@click=${this._newBackup}
|
||||
>
|
||||
${backupInProgress
|
||||
? html`<div slot="icon" class="loading">
|
||||
? html`<div slot="start" class="loading">
|
||||
<ha-spinner .size=${"small"}></ha-spinner>
|
||||
</div>`
|
||||
: html`<ha-svg-icon
|
||||
slot="icon"
|
||||
slot="start"
|
||||
.path=${mdiPlus}
|
||||
></ha-svg-icon>`}
|
||||
</ha-fab>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.backup.backups.new_backup"
|
||||
)}
|
||||
</ha-button>
|
||||
`
|
||||
: nothing}
|
||||
</hass-tabs-subpage-data-table>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user