mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 19:26:36 +00:00
20231027.0 (#18448)
This commit is contained in:
commit
3d674cf237
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20231026.0"
|
||||
version = "20231027.0"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "The Home Assistant frontend"
|
||||
readme = "README.md"
|
||||
|
@ -14,7 +14,7 @@ export const slugify = (value: string, delimiter = "_") => {
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
|
||||
.replace(/(?<=\d),(?=\d)/g, "") // Remove Commas between numbers
|
||||
.replace(/(\d),(?=\d)/g, "$1") // Remove Commas between numbers
|
||||
.replace(/[^a-z0-9]+/g, delimiter) // Replace all non-word characters
|
||||
.replace(new RegExp(`(${delimiter})\\1+`, "g"), "$1") // Replace multiple delimiters with single delimiter
|
||||
.replace(new RegExp(`^${delimiter}+`), "") // Trim delimiter from start of text
|
||||
|
@ -20,6 +20,10 @@ export class HaButton extends Button {
|
||||
.trailing-icon {
|
||||
display: flex;
|
||||
}
|
||||
.slot-container {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -46,6 +46,8 @@ export class HaDateRangePicker extends LitElement {
|
||||
|
||||
@property() public ranges?: DateRangePickerRanges | false;
|
||||
|
||||
@state() private _ranges?: DateRangePickerRanges;
|
||||
|
||||
@property() public autoApply = false;
|
||||
|
||||
@property() public timePicker = true;
|
||||
@ -93,7 +95,7 @@ export class HaDateRangePicker extends LitElement {
|
||||
}
|
||||
);
|
||||
|
||||
this.ranges = {
|
||||
this._ranges = {
|
||||
[this.hass.localize("ui.components.date-range-picker.ranges.today")]: [
|
||||
calcDate(today, startOfDay, this.hass.locale, this.hass.config, {
|
||||
weekStartsOn,
|
||||
@ -206,15 +208,15 @@ export class HaDateRangePicker extends LitElement {
|
||||
.path=${mdiCalendar}
|
||||
></ha-icon-button>`}
|
||||
</div>
|
||||
${this.ranges
|
||||
${this.ranges !== false && (this.ranges || this._ranges)
|
||||
? html`<div
|
||||
slot="ranges"
|
||||
class="date-range-ranges"
|
||||
.dir=${this._rtlDirection}
|
||||
>
|
||||
<mwc-list @action=${this._setDateRange} activatable>
|
||||
${Object.keys(this.ranges).map(
|
||||
(name) => html`<mwc-list-item> ${name} </mwc-list-item>`
|
||||
${Object.keys(this.ranges || this._ranges!).map(
|
||||
(name) => html`<mwc-list-item>${name}</mwc-list-item>`
|
||||
)}
|
||||
</mwc-list>
|
||||
</div>`
|
||||
@ -234,7 +236,9 @@ export class HaDateRangePicker extends LitElement {
|
||||
}
|
||||
|
||||
private _setDateRange(ev: CustomEvent<ActionDetail>) {
|
||||
const dateRange = Object.values(this.ranges!)[ev.detail.index];
|
||||
const dateRange = Object.values(this.ranges || this._ranges!)[
|
||||
ev.detail.index
|
||||
];
|
||||
const dateRangePicker = this._dateRangePicker;
|
||||
dateRangePicker.clickRange(dateRange);
|
||||
dateRangePicker.clickedApply();
|
||||
|
@ -94,6 +94,8 @@ export class HaDialog extends DialogBase {
|
||||
}
|
||||
.mdc-dialog__title {
|
||||
padding: 24px 24px 0 24px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mdc-dialog__actions {
|
||||
padding: 12px 24px 12px 24px;
|
||||
|
@ -200,6 +200,8 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
||||
|
||||
this._name = this.entry.name || "";
|
||||
this._icon = this.entry.icon || "";
|
||||
this._deviceClass =
|
||||
this.entry.device_class || this.entry.original_device_class;
|
||||
this._origEntityId = this.entry.entity_id;
|
||||
this._areaId = this.entry.area_id;
|
||||
this._entityId = this.entry.entity_id;
|
||||
|
@ -1,5 +1,12 @@
|
||||
import { mdiPlus } from "@mdi/js";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoize from "memoize-one";
|
||||
import { stringCompare } from "../../../../common/string/compare";
|
||||
@ -7,6 +14,7 @@ import {
|
||||
DataTableColumnContainer,
|
||||
RowClickedEvent,
|
||||
} from "../../../../components/data-table/ha-data-table";
|
||||
import "../../../../components/ha-card";
|
||||
import "../../../../components/ha-fab";
|
||||
import "../../../../components/ha-svg-icon";
|
||||
import {
|
||||
@ -21,7 +29,9 @@ import {
|
||||
showConfirmationDialog,
|
||||
} from "../../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../../layouts/hass-loading-screen";
|
||||
import "../../../../layouts/hass-subpage";
|
||||
import "../../../../layouts/hass-tabs-subpage-data-table";
|
||||
import { haStyle } from "../../../../resources/styles";
|
||||
import { HomeAssistant, Route } from "../../../../types";
|
||||
import { loadLovelaceResources } from "../../../lovelace/common/load-resources";
|
||||
import { lovelaceTabs } from "../ha-config-lovelace";
|
||||
@ -72,6 +82,36 @@ export class HaConfigLovelaceRescources extends LitElement {
|
||||
return html` <hass-loading-screen></hass-loading-screen> `;
|
||||
}
|
||||
|
||||
if (this.hass.config.safe_mode) {
|
||||
return html`
|
||||
<hass-subpage
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
back-path="/config"
|
||||
.header=${this.hass.localize(
|
||||
"ui.panel.config.lovelace.resources.caption"
|
||||
)}
|
||||
>
|
||||
<div class="content">
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<h2>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.lovelace.resources.unavailable"
|
||||
)}
|
||||
</h2>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.lovelace.resources.unavailable_safe_mode"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
</hass-subpage>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<hass-tabs-subpage-data-table
|
||||
.hass=${this.hass}
|
||||
@ -192,4 +232,24 @@ export class HaConfigLovelaceRescources extends LitElement {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
.content {
|
||||
padding: 28px 20px 0;
|
||||
max-width: 1040px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h2 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ class HaPanelDevState extends LitElement {
|
||||
autofocus
|
||||
.hass=${this.hass}
|
||||
.value=${this._entityId}
|
||||
@change=${this._entityIdChanged}
|
||||
@value-changed=${this._entityIdChanged}
|
||||
allow-custom-entity
|
||||
item-label-path="entity_id"
|
||||
></ha-entity-picker>
|
||||
@ -347,7 +347,8 @@ class HaPanelDevState extends LitElement {
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
private _entityIdChanged() {
|
||||
private _entityIdChanged(ev: CustomEvent) {
|
||||
this._entityId = ev.detail.value;
|
||||
if (!this._entityId) {
|
||||
this._entity = undefined;
|
||||
this._state = "";
|
||||
|
@ -48,6 +48,9 @@ class PanelEnergy extends LitElement {
|
||||
if (oldHass?.locale !== this.hass.locale) {
|
||||
this._setLovelace();
|
||||
}
|
||||
if (oldHass && oldHass.localize !== this.hass.localize) {
|
||||
this._reloadView();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
|
@ -135,10 +135,14 @@ export class HuiTodoListCard
|
||||
|
||||
public hassSubscribe(): Promise<UnsubscribeFunc>[] {
|
||||
return [
|
||||
this.hass!.connection.subscribeEvents(
|
||||
() => this._fetchData(),
|
||||
"shopping_list_updated"
|
||||
),
|
||||
this.hass!.connection.subscribeEvents(() => {
|
||||
if (
|
||||
this._entityId &&
|
||||
this.hass!.entities[this._entityId]?.platform === "shopping_list"
|
||||
) {
|
||||
this._fetchData();
|
||||
}
|
||||
}, "shopping_list_updated"),
|
||||
];
|
||||
}
|
||||
|
||||
@ -159,6 +163,15 @@ export class HuiTodoListCard
|
||||
) {
|
||||
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
||||
}
|
||||
|
||||
if (
|
||||
this._entityId &&
|
||||
oldHass &&
|
||||
oldHass.states[this._entityId] !== this.hass.states[this._entityId] &&
|
||||
this.hass.entities[this._entityId]?.platform !== "shopping_list"
|
||||
) {
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
|
@ -10,11 +10,6 @@ export const loadLovelaceResources = (
|
||||
resources: NonNullable<LovelaceResource[]>,
|
||||
hass: HomeAssistant
|
||||
) => {
|
||||
// Don't load ressources on safe mode
|
||||
// Sometimes, hass.config is null but it should not.
|
||||
if (hass.config?.safe_mode) {
|
||||
return;
|
||||
}
|
||||
resources.forEach((resource) => {
|
||||
const normalizedUrl = new URL(
|
||||
resource.url,
|
||||
|
@ -65,7 +65,7 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
|
||||
|
||||
@state() _endDate?: Date;
|
||||
|
||||
@state() private _ranges?: DateRangePickerRanges;
|
||||
@state() private _ranges: DateRangePickerRanges = {};
|
||||
|
||||
@state() private _compare = false;
|
||||
|
||||
|
@ -2,10 +2,10 @@ import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||
import "@material/mwc-list";
|
||||
import {
|
||||
mdiChevronDown,
|
||||
mdiCommentProcessingOutline,
|
||||
mdiDelete,
|
||||
mdiDotsVertical,
|
||||
mdiInformationOutline,
|
||||
mdiMicrophone,
|
||||
mdiPlus,
|
||||
} from "@mdi/js";
|
||||
import {
|
||||
@ -173,11 +173,13 @@ class PanelTodo extends LitElement {
|
||||
.x=${this.mobile ? 0 : undefined}
|
||||
>
|
||||
<ha-button slot="trigger">
|
||||
<div>
|
||||
${this._entityId
|
||||
? this._entityId in this.hass.states
|
||||
? computeStateName(this.hass.states[this._entityId])
|
||||
: this._entityId
|
||||
: ""}
|
||||
</div>
|
||||
<ha-svg-icon
|
||||
slot="trailingIcon"
|
||||
.path=${mdiChevronDown}
|
||||
@ -190,7 +192,7 @@ class PanelTodo extends LitElement {
|
||||
${this.hass.localize("ui.panel.todo.create_list")}
|
||||
</ha-list-item>
|
||||
</ha-button-menu>`
|
||||
: "Lists"}
|
||||
: this.hass.localize("panel.todo")}
|
||||
</div>
|
||||
<mwc-list slot="pane" activatable>${listItems}</mwc-list>
|
||||
<ha-list-item graphic="icon" slot="pane-footer" @click=${this._addList}>
|
||||
@ -216,8 +218,9 @@ class PanelTodo extends LitElement {
|
||||
: nothing}
|
||||
<li divider role="separator"></li>
|
||||
<ha-list-item graphic="icon" @click=${this._showVoiceCommandDialog}>
|
||||
<ha-svg-icon .path=${mdiMicrophone} slot="graphic"> </ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.todo.start_conversation")}
|
||||
<ha-svg-icon .path=${mdiCommentProcessingOutline} slot="graphic">
|
||||
</ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.todo.assist")}
|
||||
</ha-list-item>
|
||||
${entityRegistryEntry?.platform === "local_todo"
|
||||
? html` <li divider role="separator"></li>
|
||||
@ -335,11 +338,18 @@ class PanelTodo extends LitElement {
|
||||
:host([mobile]) .lists {
|
||||
--mdc-menu-min-width: 100vw;
|
||||
}
|
||||
:host(:not([mobile])) .lists ha-list-item {
|
||||
max-width: calc(100vw - 120px);
|
||||
}
|
||||
:host([mobile]) ha-button-menu {
|
||||
--mdc-shape-medium: 0 0 var(--mdc-shape-medium)
|
||||
var(--mdc-shape-medium);
|
||||
}
|
||||
ha-button-menu {
|
||||
max-width: 100%;
|
||||
}
|
||||
ha-button-menu ha-button {
|
||||
max-width: 100%;
|
||||
--mdc-theme-primary: currentColor;
|
||||
--mdc-typography-button-text-transform: none;
|
||||
--mdc-typography-button-font-size: var(
|
||||
@ -360,6 +370,13 @@ class PanelTodo extends LitElement {
|
||||
);
|
||||
--button-height: 40px;
|
||||
}
|
||||
ha-button-menu ha-button div {
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -274,6 +274,10 @@ export const connectionMixin = <T extends Constructor<HassBaseEl>>(
|
||||
// on reconnect always fetch config as we might miss an update while we were disconnected
|
||||
// @ts-ignore
|
||||
this.hass!.callWS({ type: "get_config" }).then((config: HassConfig) => {
|
||||
if (config.safe_mode) {
|
||||
// @ts-ignore Firefox supports forceGet
|
||||
location.reload(true);
|
||||
}
|
||||
this._updateHass({ config });
|
||||
this.checkDataBaseMigration();
|
||||
});
|
||||
|
@ -2140,6 +2140,8 @@
|
||||
"js": "JavaScript file (deprecated)",
|
||||
"module": "JavaScript module"
|
||||
},
|
||||
"unavailable": "Resources unavailable",
|
||||
"unavailable_safe_mode": "Resources are not available in safe mode",
|
||||
"picker": {
|
||||
"headers": {
|
||||
"url": "URL",
|
||||
@ -4520,7 +4522,6 @@
|
||||
"never_triggered": "Never triggered"
|
||||
},
|
||||
"todo-list": {
|
||||
"lists": "To-do Lists",
|
||||
"checked_items": "Checked items",
|
||||
"clear_items": "Clear checked items",
|
||||
"add_item": "Add item",
|
||||
@ -5094,7 +5095,7 @@
|
||||
"description": "The Sensor card gives you a quick overview of your sensors state with an optional graph to visualize change over time."
|
||||
},
|
||||
"todo-list": {
|
||||
"name": "Todo list",
|
||||
"name": "To-do list",
|
||||
"description": "The to-do list card allows you to add, edit, check-off, and clear items from your to-do list.",
|
||||
"integration_not_loaded": "This card requires the `todo` integration to be set up."
|
||||
},
|
||||
@ -5515,7 +5516,7 @@
|
||||
}
|
||||
},
|
||||
"todo": {
|
||||
"start_conversation": "Start conversation",
|
||||
"assist": "[%key:ui::panel::lovelace::menu::assist%]",
|
||||
"create_list": "Create list",
|
||||
"delete_list": "Delete list",
|
||||
"information": "Information",
|
||||
|
Loading…
x
Reference in New Issue
Block a user