mirror of
https://github.com/home-assistant/frontend.git
synced 2025-10-16 23:29:41 +00:00
Compare commits
31 Commits
section-vi
...
bottom-she
Author | SHA1 | Date | |
---|---|---|---|
![]() |
459a0bd178 | ||
![]() |
d808c63c55 | ||
![]() |
f32ca9be29 | ||
![]() |
8c4c4157a8 | ||
![]() |
c8419d4c3d | ||
![]() |
80135af9ec | ||
![]() |
848b737a92 | ||
![]() |
50d4fae14a | ||
![]() |
1da3570a6f | ||
![]() |
089316b8ae | ||
![]() |
6c4046c842 | ||
![]() |
8d03ac5f64 | ||
![]() |
e0e1f6f920 | ||
![]() |
d4c98cae3a | ||
![]() |
46d0eb4f44 | ||
![]() |
e97b04b5b9 | ||
![]() |
6dbfb60825 | ||
![]() |
07812f8d84 | ||
![]() |
96f54d348f | ||
![]() |
6084ab116f | ||
![]() |
6b7acd8d3b | ||
![]() |
ba2881b0bf | ||
![]() |
e42be13682 | ||
![]() |
bf8dacb7a4 | ||
![]() |
e35b155c66 | ||
![]() |
437d02c12f | ||
![]() |
a23fc8fcba | ||
![]() |
0ba88246a1 | ||
![]() |
6529b31b65 | ||
![]() |
866518477f | ||
![]() |
f831f876de |
@@ -34,7 +34,7 @@
|
||||
"@codemirror/legacy-modes": "6.5.2",
|
||||
"@codemirror/search": "6.5.11",
|
||||
"@codemirror/state": "6.5.2",
|
||||
"@codemirror/view": "6.38.5",
|
||||
"@codemirror/view": "6.38.6",
|
||||
"@date-fns/tz": "1.4.1",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "6.18.2",
|
||||
@@ -52,7 +52,7 @@
|
||||
"@fullcalendar/list": "6.1.19",
|
||||
"@fullcalendar/luxon3": "6.1.19",
|
||||
"@fullcalendar/timegrid": "6.1.19",
|
||||
"@home-assistant/webawesome": "3.0.0-beta.6.ha.4",
|
||||
"@home-assistant/webawesome": "3.0.0-beta.6.ha.5",
|
||||
"@lezer/highlight": "1.2.1",
|
||||
"@lit-labs/motion": "1.0.9",
|
||||
"@lit-labs/observers": "2.0.6",
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiDrag, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@@ -129,7 +129,7 @@ export class DialogDataTableSettings extends LitElement {
|
||||
${canMove && isVisible
|
||||
? html`<ha-svg-icon
|
||||
class="handle"
|
||||
.path=${mdiDrag}
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
slot="graphic"
|
||||
></ha-svg-icon>`
|
||||
: nothing}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
import { mdiDrag } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { isValidEntityId } from "../../common/entity/valid_entity_id";
|
||||
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../types";
|
||||
import "../ha-sortable";
|
||||
import "./ha-entity-picker";
|
||||
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
|
||||
|
||||
@customElement("ha-entities-picker")
|
||||
class HaEntitiesPicker extends LitElement {
|
||||
@@ -118,7 +118,7 @@ class HaEntitiesPicker extends LitElement {
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
class="entity-handle"
|
||||
.path=${mdiDrag}
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: nothing}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import "@material/mwc-menu/mwc-menu-surface";
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant, mdiPlus } from "@mdi/js";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import type { IFuseOptions } from "fuse.js";
|
||||
import Fuse from "fuse.js";
|
||||
@@ -86,8 +86,8 @@ export class HaEntityNamePicker extends LitElement {
|
||||
|
||||
private _editIndex?: number;
|
||||
|
||||
private _validOptions = memoizeOne((entityId?: string) => {
|
||||
const options = new Set<string>();
|
||||
private _validTypes = memoizeOne((entityId?: string) => {
|
||||
const options = new Set<string>(["text"]);
|
||||
if (!entityId) {
|
||||
return options;
|
||||
}
|
||||
@@ -119,22 +119,22 @@ export class HaEntityNamePicker extends LitElement {
|
||||
return [];
|
||||
}
|
||||
|
||||
const options = this._validOptions(entityId);
|
||||
const types = this._validTypes(entityId);
|
||||
|
||||
const items = (
|
||||
["entity", "device", "area", "floor"] as const
|
||||
).map<EntityNameOption>((name) => {
|
||||
const stateObj = this.hass.states[entityId];
|
||||
const isValid = options.has(name);
|
||||
const isValid = types.has(name);
|
||||
const primary = this.hass.localize(
|
||||
`ui.components.entity.entity-name-picker.types.${name}`
|
||||
);
|
||||
const secondary =
|
||||
stateObj && isValid
|
||||
(stateObj && isValid
|
||||
? this.hass.formatEntityName(stateObj, { type: name })
|
||||
: this.hass.localize(
|
||||
`ui.components.entity.entity-name-picker.types.${name}_missing` as LocalizeKeys
|
||||
) || "-";
|
||||
)) || "-";
|
||||
|
||||
return {
|
||||
primary,
|
||||
@@ -169,9 +169,9 @@ export class HaEntityNamePicker extends LitElement {
|
||||
};
|
||||
|
||||
protected render() {
|
||||
const value = this._value;
|
||||
const value = this._items;
|
||||
const options = this._getOptions(this.entityId);
|
||||
const validOptions = this._validOptions(this.entityId);
|
||||
const validTypes = this._validTypes(this.entityId);
|
||||
|
||||
return html`
|
||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
@@ -185,12 +185,11 @@ export class HaEntityNamePicker extends LitElement {
|
||||
>
|
||||
<ha-chip-set>
|
||||
${repeat(
|
||||
this._value,
|
||||
this._items,
|
||||
(item) => item,
|
||||
(item: EntityNameItem, idx) => {
|
||||
const label = this._formatItem(item);
|
||||
const isValid =
|
||||
item.type === "text" || validOptions.has(item.type);
|
||||
const isValid = validTypes.has(item.type);
|
||||
return html`
|
||||
<ha-input-chip
|
||||
data-idx=${idx}
|
||||
@@ -201,7 +200,10 @@ export class HaEntityNamePicker extends LitElement {
|
||||
.disabled=${this.disabled}
|
||||
class=${!isValid ? "invalid" : ""}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
<span>${label}</span>
|
||||
</ha-input-chip>
|
||||
`;
|
||||
@@ -235,7 +237,7 @@ export class HaEntityNamePicker extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.value=${""}
|
||||
.autofocus=${this.autofocus}
|
||||
.disabled=${this.disabled || !this.entityId}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required && !value.length}
|
||||
.helper=${this.helper}
|
||||
.items=${options}
|
||||
@@ -282,13 +284,16 @@ export class HaEntityNamePicker extends LitElement {
|
||||
this._opened = true;
|
||||
}
|
||||
|
||||
private get _value(): EntityNameItem[] {
|
||||
private get _items(): EntityNameItem[] {
|
||||
return this._toItems(this.value);
|
||||
}
|
||||
|
||||
private _toItems = memoizeOne((value?: typeof this.value) => {
|
||||
if (typeof value === "string") {
|
||||
return [{ type: "text", text: value } as const];
|
||||
if (value === "") {
|
||||
return [];
|
||||
}
|
||||
return [{ type: "text", text: value } satisfies EntityNameItem];
|
||||
}
|
||||
return value ? ensureArray(value) : [];
|
||||
});
|
||||
@@ -296,7 +301,7 @@ export class HaEntityNamePicker extends LitElement {
|
||||
private _toValue = memoizeOne(
|
||||
(items: EntityNameItem[]): typeof this.value => {
|
||||
if (items.length === 0) {
|
||||
return [];
|
||||
return "";
|
||||
}
|
||||
if (items.length === 1) {
|
||||
const item = items[0];
|
||||
@@ -312,19 +317,21 @@ export class HaEntityNamePicker extends LitElement {
|
||||
const options = this._comboBox.items || [];
|
||||
|
||||
const initialItem =
|
||||
this._editIndex != null ? this._value[this._editIndex] : undefined;
|
||||
this._editIndex != null ? this._items[this._editIndex] : undefined;
|
||||
|
||||
const initialValue = initialItem ? formatOptionValue(initialItem) : "";
|
||||
|
||||
const filteredItems = this._filterSelectedOptions(options, initialValue);
|
||||
|
||||
if (initialItem && initialItem.type === "text" && initialItem.text) {
|
||||
if (initialItem?.type === "text" && initialItem.text) {
|
||||
filteredItems.push(this._customNameOption(initialItem.text));
|
||||
}
|
||||
|
||||
this._comboBox.filteredItems = filteredItems;
|
||||
this._comboBox.setInputValue(initialValue);
|
||||
} else {
|
||||
this._opened = false;
|
||||
this._comboBox.setInputValue("");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,15 +339,16 @@ export class HaEntityNamePicker extends LitElement {
|
||||
options: EntityNameOption[],
|
||||
current?: string
|
||||
) => {
|
||||
const value = this._value;
|
||||
const items = this._items;
|
||||
|
||||
const types = value.map((item) => item.type) as string[];
|
||||
const excludedValues = new Set(
|
||||
items
|
||||
.filter((item) => UNIQUE_TYPES.has(item.type))
|
||||
.map((item) => formatOptionValue(item))
|
||||
);
|
||||
|
||||
const filteredOptions = options.filter(
|
||||
(option) =>
|
||||
!UNIQUE_TYPES.has(option.value) ||
|
||||
!types.includes(option.value) ||
|
||||
option.value === current
|
||||
(option) => !excludedValues.has(option.value) || option.value === current
|
||||
);
|
||||
return filteredOptions;
|
||||
};
|
||||
@@ -351,16 +359,14 @@ export class HaEntityNamePicker extends LitElement {
|
||||
const options = this._comboBox.items || [];
|
||||
|
||||
const currentItem =
|
||||
this._editIndex != null ? this._value[this._editIndex] : undefined;
|
||||
this._editIndex != null ? this._items[this._editIndex] : undefined;
|
||||
|
||||
const currentValue = currentItem ? formatOptionValue(currentItem) : "";
|
||||
|
||||
this._comboBox.filteredItems = this._filterSelectedOptions(
|
||||
options,
|
||||
currentValue
|
||||
);
|
||||
let filteredItems = this._filterSelectedOptions(options, currentValue);
|
||||
|
||||
if (!filter) {
|
||||
this._comboBox.filteredItems = filteredItems;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -372,9 +378,8 @@ export class HaEntityNamePicker extends LitElement {
|
||||
ignoreDiacritics: true,
|
||||
};
|
||||
|
||||
const fuse = new Fuse(this._comboBox.filteredItems, fuseOptions);
|
||||
const filteredItems = fuse.search(filter).map((result) => result.item);
|
||||
|
||||
const fuse = new Fuse(filteredItems, fuseOptions);
|
||||
filteredItems = fuse.search(filter).map((result) => result.item);
|
||||
filteredItems.push(this._customNameOption(input));
|
||||
this._comboBox.filteredItems = filteredItems;
|
||||
}
|
||||
@@ -382,7 +387,7 @@ export class HaEntityNamePicker extends LitElement {
|
||||
private async _moveItem(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
const { oldIndex, newIndex } = ev.detail;
|
||||
const value = this._value;
|
||||
const value = this._items;
|
||||
const newValue = value.concat();
|
||||
const element = newValue.splice(oldIndex, 1)[0];
|
||||
newValue.splice(newIndex, 0, element);
|
||||
@@ -393,7 +398,7 @@ export class HaEntityNamePicker extends LitElement {
|
||||
|
||||
private async _removeItem(ev) {
|
||||
ev.stopPropagation();
|
||||
const value = [...this._value];
|
||||
const value = [...this._items];
|
||||
const idx = parseInt(ev.target.dataset.idx, 10);
|
||||
value.splice(idx, 1);
|
||||
this._setValue(value);
|
||||
@@ -411,7 +416,7 @@ export class HaEntityNamePicker extends LitElement {
|
||||
|
||||
const item: EntityNameItem = parseOptionValue(value);
|
||||
|
||||
const newValue = [...this._value];
|
||||
const newValue = [...this._items];
|
||||
|
||||
if (this._editIndex != null) {
|
||||
newValue[this._editIndex] = item;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiDrag } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant } from "@mdi/js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
@@ -195,7 +195,10 @@ class HaEntityStatePicker extends LitElement {
|
||||
.label=${label}
|
||||
selected
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
${label}
|
||||
</ha-input-chip>
|
||||
`;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiDrag, mdiTextureBox } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant, mdiTextureBox } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
@@ -105,7 +105,7 @@ export class HaAreasFloorsDisplayEditor extends LitElement {
|
||||
<ha-svg-icon
|
||||
class="handle"
|
||||
slot="icons"
|
||||
.path=${mdiDrag}
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
`}
|
||||
<ha-items-display-editor
|
||||
|
@@ -1,6 +1,13 @@
|
||||
import "@home-assistant/webawesome/dist/components/drawer/drawer";
|
||||
import { css, html, LitElement, type PropertyValues } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
customElement,
|
||||
eventOptions,
|
||||
property,
|
||||
query,
|
||||
state,
|
||||
} from "lit/decorators";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
|
||||
export const BOTTOM_SHEET_ANIMATION_DURATION_MS = 300;
|
||||
|
||||
@@ -13,6 +20,16 @@ export class HaBottomSheet extends LitElement {
|
||||
|
||||
@state() private _drawerOpen = false;
|
||||
|
||||
@query(".body") public bodyContainer!: HTMLDivElement;
|
||||
|
||||
private _lockResize = false;
|
||||
|
||||
private _lockResizeByChild = false;
|
||||
|
||||
private _resizeStartY = 0;
|
||||
|
||||
private _resizeDelta = 0;
|
||||
|
||||
private _handleAfterHide() {
|
||||
this.open = false;
|
||||
const ev = new Event("closed", {
|
||||
@@ -36,54 +53,144 @@ export class HaBottomSheet extends LitElement {
|
||||
.open=${this._drawerOpen}
|
||||
@wa-after-hide=${this._handleAfterHide}
|
||||
without-header
|
||||
@touchstart=${this._handleTouchStart}
|
||||
>
|
||||
<slot></slot>
|
||||
<slot name="header"></slot>
|
||||
<div
|
||||
class="body ha-scrollbar"
|
||||
@scroll=${this._handleScroll}
|
||||
@bottom-sheet-lock-resize-changed=${this._handleLockResizeByChild}
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</wa-drawer>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
wa-drawer {
|
||||
--wa-color-surface-raised: transparent;
|
||||
--spacing: 0;
|
||||
--size: var(--ha-bottom-sheet-height, auto);
|
||||
--show-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
|
||||
--hide-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
|
||||
@eventOptions({ passive: true })
|
||||
private _handleScroll(ev: Event) {
|
||||
const target = ev.target as HTMLElement;
|
||||
this._lockResize = target.scrollTop > 0;
|
||||
}
|
||||
|
||||
private _handleLockResizeByChild = (ev: CustomEvent<boolean>) => {
|
||||
this._lockResizeByChild = ev.detail;
|
||||
|
||||
if (this._lockResizeByChild) {
|
||||
this._endResizing();
|
||||
}
|
||||
wa-drawer::part(dialog) {
|
||||
max-height: var(--ha-bottom-sheet-max-height, 90vh);
|
||||
align-items: center;
|
||||
}
|
||||
wa-drawer::part(body) {
|
||||
max-width: var(--ha-bottom-sheet-max-width);
|
||||
width: 100%;
|
||||
border-top-left-radius: var(
|
||||
--ha-bottom-sheet-border-radius,
|
||||
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
|
||||
);
|
||||
border-top-right-radius: var(
|
||||
--ha-bottom-sheet-border-radius,
|
||||
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
|
||||
);
|
||||
background-color: var(
|
||||
--ha-bottom-sheet-surface-background,
|
||||
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
|
||||
);
|
||||
padding: var(
|
||||
--ha-bottom-sheet-padding,
|
||||
0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
|
||||
var(--safe-area-inset-left)
|
||||
);
|
||||
};
|
||||
|
||||
private _handleTouchStart = (ev: TouchEvent) => {
|
||||
if (this._lockResize || this._lockResizeByChild) {
|
||||
return;
|
||||
}
|
||||
|
||||
:host([flexcontent]) wa-drawer::part(body) {
|
||||
display: flex;
|
||||
this._startResizing(ev.touches[0].clientY);
|
||||
};
|
||||
|
||||
private _startResizing(clientY: number) {
|
||||
// register event listeners for drag handling
|
||||
document.addEventListener("touchmove", this._handleMouseMove, {
|
||||
passive: false,
|
||||
});
|
||||
document.addEventListener("touchend", this._endResizing);
|
||||
document.addEventListener("touchcancel", this._endResizing);
|
||||
|
||||
this._resizeStartY = clientY;
|
||||
}
|
||||
|
||||
private _handleMouseMove = (ev: TouchEvent) => {
|
||||
this._resizeDelta = this._resizeStartY - ev.touches[0].clientY;
|
||||
if (this._resizeDelta < 0) {
|
||||
ev.preventDefault();
|
||||
requestAnimationFrame(() => {
|
||||
this.style.setProperty(
|
||||
"--dialog-transform",
|
||||
`translateY(${this._resizeDelta * -1}px)`
|
||||
);
|
||||
});
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
private _endResizing = () => {
|
||||
this._unregisterResizeHandlers();
|
||||
|
||||
if (this._resizeDelta > -50) {
|
||||
this.style.removeProperty("--dialog-transform");
|
||||
return;
|
||||
}
|
||||
|
||||
this._drawerOpen = false;
|
||||
};
|
||||
|
||||
private _unregisterResizeHandlers = () => {
|
||||
document.removeEventListener("touchmove", this._handleMouseMove);
|
||||
document.removeEventListener("touchend", this._unregisterResizeHandlers);
|
||||
document.removeEventListener("touchcancel", this._unregisterResizeHandlers);
|
||||
};
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this._unregisterResizeHandlers();
|
||||
}
|
||||
|
||||
static styles = [
|
||||
haStyleScrollbar,
|
||||
css`
|
||||
wa-drawer {
|
||||
--wa-color-surface-raised: transparent;
|
||||
--spacing: 0;
|
||||
--size: var(--ha-bottom-sheet-height, auto);
|
||||
--show-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
|
||||
--hide-duration: ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms;
|
||||
}
|
||||
wa-drawer::part(dialog) {
|
||||
max-height: var(--ha-bottom-sheet-max-height, 90vh);
|
||||
align-items: center;
|
||||
transform: var(--dialog-transform);
|
||||
}
|
||||
wa-drawer::part(body) {
|
||||
max-width: var(--ha-bottom-sheet-max-width);
|
||||
width: 100%;
|
||||
border-top-left-radius: var(
|
||||
--ha-bottom-sheet-border-radius,
|
||||
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
|
||||
);
|
||||
border-top-right-radius: var(
|
||||
--ha-bottom-sheet-border-radius,
|
||||
var(--ha-dialog-border-radius, var(--ha-border-radius-2xl))
|
||||
);
|
||||
background-color: var(
|
||||
--ha-bottom-sheet-surface-background,
|
||||
var(--ha-dialog-surface-background, var(--mdc-theme-surface, #fff)),
|
||||
);
|
||||
}
|
||||
:host([flexcontent]) wa-drawer::part(body) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
:host([flexcontent]) .body {
|
||||
flex: 1;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: var(
|
||||
--ha-bottom-sheet-padding,
|
||||
0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
|
||||
var(--safe-area-inset-left)
|
||||
);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-bottom-sheet": HaBottomSheet;
|
||||
}
|
||||
|
||||
interface HASSDomEvents {
|
||||
"bottom-sheet-lock-resize-changed": boolean;
|
||||
}
|
||||
}
|
||||
|
@@ -31,6 +31,9 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
@property({ type: Boolean, reflect: true, attribute: "no-wrap" })
|
||||
public nowrap = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true, attribute: "full-width" })
|
||||
public fullWidth = false;
|
||||
|
||||
@property() public variant:
|
||||
| "brand"
|
||||
| "neutral"
|
||||
@@ -38,6 +41,13 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
| "warning"
|
||||
| "danger" = "brand";
|
||||
|
||||
@property({ attribute: "active-variant" }) public activeVariant?:
|
||||
| "brand"
|
||||
| "neutral"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "danger";
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<wa-button-group childSelector="ha-button">
|
||||
@@ -46,7 +56,9 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
html`<ha-button
|
||||
iconTag="ha-svg-icon"
|
||||
class="icon"
|
||||
.variant=${this.variant}
|
||||
.variant=${this.active !== button.value || !this.activeVariant
|
||||
? this.variant
|
||||
: this.activeVariant}
|
||||
.size=${this.size}
|
||||
.value=${button.value}
|
||||
@click=${this._handleClick}
|
||||
@@ -78,6 +90,19 @@ export class HaButtonToggleGroup extends LitElement {
|
||||
:host([no-wrap]) wa-button-group::part(base) {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
wa-button-group {
|
||||
padding: var(--ha-button-toggle-group-padding);
|
||||
}
|
||||
|
||||
:host([full-width]) wa-button-group,
|
||||
:host([full-width]) wa-button-group::part(base) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:host([full-width]) ha-button {
|
||||
flex: 1;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -49,12 +49,16 @@ export class HaDialogHeader extends LitElement {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
padding: 0 var(--ha-space-1);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.header-content {
|
||||
flex: 1;
|
||||
padding: 10px 4px;
|
||||
padding: 10px var(--ha-space-1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-height: var(--ha-space-12);
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -63,7 +67,7 @@ export class HaDialogHeader extends LitElement {
|
||||
.header-title {
|
||||
height: var(
|
||||
--ha-dialog-header-title-height,
|
||||
calc(var(--ha-font-size-xl) + 4px)
|
||||
calc(var(--ha-font-size-xl) + var(--ha-space-1))
|
||||
);
|
||||
font-size: var(--ha-font-size-xl);
|
||||
line-height: var(--ha-line-height-condensed);
|
||||
@@ -76,19 +80,19 @@ export class HaDialogHeader extends LitElement {
|
||||
}
|
||||
@media all and (min-width: 450px) and (min-height: 500px) {
|
||||
.header-bar {
|
||||
padding: 16px;
|
||||
padding: 0 var(--ha-space-2);
|
||||
}
|
||||
}
|
||||
.header-navigation-icon {
|
||||
flex: none;
|
||||
min-width: 8px;
|
||||
min-width: var(--ha-space-2);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.header-action-items {
|
||||
flex: none;
|
||||
min-width: 8px;
|
||||
min-width: var(--ha-space-2);
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
||||
import { mdiDrag, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import type { TemplateResult } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
@@ -178,7 +178,7 @@ export class HaItemDisplayEditor extends LitElement {
|
||||
? this._dragHandleKeydown
|
||||
: undefined}
|
||||
class="handle"
|
||||
.path=${mdiDrag}
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
slot="end"
|
||||
></ha-svg-icon>
|
||||
`
|
||||
|
@@ -1,152 +0,0 @@
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { ImageSelector } from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-textarea";
|
||||
import "../ha-textfield";
|
||||
import "../ha-picture-upload";
|
||||
import "../ha-radio";
|
||||
import "../ha-formfield";
|
||||
import type { HaPictureUpload } from "../ha-picture-upload";
|
||||
import { URL_PREFIX } from "../../data/image_upload";
|
||||
|
||||
@customElement("ha-selector-image")
|
||||
export class HaImageSelector extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public value?: any;
|
||||
|
||||
@property() public name?: string;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public placeholder?: string;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property({ attribute: false }) public selector!: ImageSelector;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public required = true;
|
||||
|
||||
@state() private showUpload = false;
|
||||
|
||||
protected firstUpdated(changedProps): void {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
if (!this.value || this.value.startsWith(URL_PREFIX)) {
|
||||
this.showUpload = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<div>
|
||||
<label>
|
||||
${this.hass.localize(
|
||||
"ui.components.selectors.image.select_image_with_label",
|
||||
{
|
||||
label:
|
||||
this.label ||
|
||||
this.hass.localize("ui.components.selectors.image.image"),
|
||||
}
|
||||
)}
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize("ui.components.selectors.image.upload")}
|
||||
>
|
||||
<ha-radio
|
||||
name="mode"
|
||||
value="upload"
|
||||
.checked=${this.showUpload}
|
||||
@change=${this._radioGroupPicked}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
<ha-formfield
|
||||
.label=${this.hass.localize("ui.components.selectors.image.url")}
|
||||
>
|
||||
<ha-radio
|
||||
name="mode"
|
||||
value="url"
|
||||
.checked=${!this.showUpload}
|
||||
@change=${this._radioGroupPicked}
|
||||
></ha-radio>
|
||||
</ha-formfield>
|
||||
</label>
|
||||
${!this.showUpload
|
||||
? html`
|
||||
<ha-textfield
|
||||
.name=${this.name}
|
||||
.value=${this.value || ""}
|
||||
.placeholder=${this.placeholder || ""}
|
||||
.helper=${this.helper}
|
||||
helperPersistent
|
||||
.disabled=${this.disabled}
|
||||
@input=${this._handleChange}
|
||||
.label=${this.label || ""}
|
||||
.required=${this.required}
|
||||
></ha-textfield>
|
||||
`
|
||||
: html`
|
||||
<ha-picture-upload
|
||||
.hass=${this.hass}
|
||||
.value=${this.value?.startsWith(URL_PREFIX) ? this.value : null}
|
||||
.original=${this.selector.image?.original}
|
||||
.cropOptions=${this.selector.image?.crop}
|
||||
select-media
|
||||
@change=${this._pictureChanged}
|
||||
></ha-picture-upload>
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _radioGroupPicked(ev): void {
|
||||
this.showUpload = ev.target.value === "upload";
|
||||
}
|
||||
|
||||
private _pictureChanged(ev) {
|
||||
const value = (ev.target as HaPictureUpload).value;
|
||||
|
||||
fireEvent(this, "value-changed", { value: value ?? undefined });
|
||||
}
|
||||
|
||||
private _handleChange(ev) {
|
||||
let value = ev.target.value;
|
||||
if (this.value === value) {
|
||||
return;
|
||||
}
|
||||
if (value === "" && !this.required) {
|
||||
value = undefined;
|
||||
}
|
||||
|
||||
fireEvent(this, "value-changed", { value });
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
ha-textarea,
|
||||
ha-textfield {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-selector-image": HaImageSelector;
|
||||
}
|
||||
}
|
@@ -1,4 +1,9 @@
|
||||
import { mdiClose, mdiDelete, mdiDrag, mdiPencil } from "@mdi/js";
|
||||
import {
|
||||
mdiClose,
|
||||
mdiDelete,
|
||||
mdiDragHorizontalVariant,
|
||||
mdiPencil,
|
||||
} from "@mdi/js";
|
||||
import { css, html, LitElement, nothing, type PropertyValues } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -92,7 +97,7 @@ export class HaObjectSelector extends LitElement {
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
class="handle"
|
||||
.path=${mdiDrag}
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
slot="start"
|
||||
></ha-svg-icon>
|
||||
`
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiDrag } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant } from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
@@ -197,7 +197,7 @@ export class HaSelectSelector extends LitElement {
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiDrag}
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: nothing}
|
||||
|
@@ -34,7 +34,6 @@ const LOAD_ELEMENTS = {
|
||||
file: () => import("./ha-selector-file"),
|
||||
floor: () => import("./ha-selector-floor"),
|
||||
label: () => import("./ha-selector-label"),
|
||||
image: () => import("./ha-selector-image"),
|
||||
background: () => import("./ha-selector-background"),
|
||||
language: () => import("./ha-selector-language"),
|
||||
navigation: () => import("./ha-selector-navigation"),
|
||||
|
@@ -36,8 +36,6 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
|
||||
@property({ attribute: false }) public value?: HassServiceTarget;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public compact = false;
|
||||
@@ -101,7 +99,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
(floor_id) => html`
|
||||
<ha-target-picker-value-chip
|
||||
.hass=${this.hass}
|
||||
.type=${"floor"}
|
||||
type="floor"
|
||||
.itemId=${floor_id}
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@expand-target-item=${this._handleExpand}
|
||||
@@ -114,7 +112,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
(area_id) => html`
|
||||
<ha-target-picker-value-chip
|
||||
.hass=${this.hass}
|
||||
.type=${"area"}
|
||||
type="area"
|
||||
.itemId=${area_id}
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@expand-target-item=${this._handleExpand}
|
||||
@@ -127,7 +125,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
(device_id) => html`
|
||||
<ha-target-picker-value-chip
|
||||
.hass=${this.hass}
|
||||
.type=${"device"}
|
||||
type="device"
|
||||
.itemId=${device_id}
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@expand-target-item=${this._handleExpand}
|
||||
@@ -140,7 +138,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
(entity_id) => html`
|
||||
<ha-target-picker-value-chip
|
||||
.hass=${this.hass}
|
||||
.type=${"entity"}
|
||||
type="entity"
|
||||
.itemId=${entity_id}
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@expand-target-item=${this._handleExpand}
|
||||
@@ -153,7 +151,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
(label_id) => html`
|
||||
<ha-target-picker-value-chip
|
||||
.hass=${this.hass}
|
||||
.type=${"label"}
|
||||
type="label"
|
||||
.itemId=${label_id}
|
||||
@remove-target-item=${this._handleRemove}
|
||||
@expand-target-item=${this._handleExpand}
|
||||
@@ -173,7 +171,6 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
type="entity"
|
||||
.hass=${this.hass}
|
||||
.items=${{ entity: ensureArray(this.value?.entity_id) }}
|
||||
.collapsed=${this.compact}
|
||||
.deviceFilter=${this.deviceFilter}
|
||||
.entityFilter=${this.entityFilter}
|
||||
.includeDomains=${this.includeDomains}
|
||||
@@ -189,7 +186,6 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
type="device"
|
||||
.hass=${this.hass}
|
||||
.items=${{ device: ensureArray(this.value?.device_id) }}
|
||||
.collapsed=${this.compact}
|
||||
.deviceFilter=${this.deviceFilter}
|
||||
.entityFilter=${this.entityFilter}
|
||||
.includeDomains=${this.includeDomains}
|
||||
@@ -208,7 +204,6 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
floor: ensureArray(this.value?.floor_id),
|
||||
area: ensureArray(this.value?.area_id),
|
||||
}}
|
||||
.collapsed=${this.compact}
|
||||
.deviceFilter=${this.deviceFilter}
|
||||
.entityFilter=${this.entityFilter}
|
||||
.includeDomains=${this.includeDomains}
|
||||
@@ -224,7 +219,6 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
type="label"
|
||||
.hass=${this.hass}
|
||||
.items=${{ label: ensureArray(this.value?.label_id) }}
|
||||
.collapsed=${this.compact}
|
||||
.deviceFilter=${this.deviceFilter}
|
||||
.entityFilter=${this.entityFilter}
|
||||
.includeDomains=${this.includeDomains}
|
||||
@@ -277,6 +271,12 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
auto-size-padding="16"
|
||||
@wa-after-show=${this._showSelector}
|
||||
@wa-after-hide=${this._hidePicker}
|
||||
trap-focus
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.components.target-picker.add_target"
|
||||
)}
|
||||
>
|
||||
${this._renderTargetSelector()}
|
||||
</wa-popover>
|
||||
@@ -287,6 +287,11 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
.open=${this._pickerWrapperOpen}
|
||||
@wa-after-show=${this._showSelector}
|
||||
@closed=${this._hidePicker}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label=${this.hass.localize(
|
||||
"ui.components.target-picker.add_target"
|
||||
)}
|
||||
>
|
||||
${this._renderTargetSelector(true)}
|
||||
</ha-bottom-sheet>`
|
||||
@@ -394,6 +399,12 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
: { [typeId]: id },
|
||||
});
|
||||
|
||||
this.shadowRoot
|
||||
?.querySelector(
|
||||
`ha-target-picker-item-group[type='${this._newTarget?.type}']`
|
||||
)
|
||||
?.removeAttribute("collapsed");
|
||||
}
|
||||
|
||||
private _handleTargetPicked = async (
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "@home-assistant/webawesome/dist/components/dialog/dialog";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import "./ha-dialog-header";
|
||||
import "./ha-icon-button";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { haStyleScrollbar } from "../resources/styles";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import "./ha-dialog-header";
|
||||
import "./ha-icon-button";
|
||||
|
||||
export type DialogWidth = "small" | "medium" | "large" | "full";
|
||||
|
||||
@@ -90,6 +90,8 @@ export class HaWaDialog extends LitElement {
|
||||
@state()
|
||||
private _open = false;
|
||||
|
||||
@query(".body") public bodyContainer!: HTMLDivElement;
|
||||
|
||||
protected updated(
|
||||
changedProperties: Map<string | number | symbol, unknown>
|
||||
): void {
|
||||
@@ -107,6 +109,7 @@ export class HaWaDialog extends LitElement {
|
||||
.lightDismiss=${!this.preventScrimClose}
|
||||
without-header
|
||||
@wa-show=${this._handleShow}
|
||||
@wa-after-show=${this._handleAfterShow}
|
||||
@wa-after-hide=${this._handleAfterHide}
|
||||
>
|
||||
<slot name="header">
|
||||
@@ -146,6 +149,10 @@ export class HaWaDialog extends LitElement {
|
||||
(this.querySelector("[autofocus]") as HTMLElement | null)?.focus();
|
||||
};
|
||||
|
||||
private _handleAfterShow = () => {
|
||||
fireEvent(this, "after-show");
|
||||
};
|
||||
|
||||
private _handleAfterHide = () => {
|
||||
this._open = false;
|
||||
fireEvent(this, "closed");
|
||||
@@ -172,7 +179,7 @@ export class HaWaDialog extends LitElement {
|
||||
)
|
||||
)
|
||||
);
|
||||
--width: var(--ha-dialog-width-md, min(580px, var(--full-width)));
|
||||
--width: min(var(--ha-dialog-width-md, 580px), var(--full-width));
|
||||
--spacing: var(--dialog-content-padding, var(--ha-space-6));
|
||||
--show-duration: var(--ha-dialog-show-duration, 200ms);
|
||||
--hide-duration: var(--ha-dialog-hide-duration, 200ms);
|
||||
@@ -193,11 +200,11 @@ export class HaWaDialog extends LitElement {
|
||||
}
|
||||
|
||||
:host([width="small"]) wa-dialog {
|
||||
--width: var(--ha-dialog-width-sm, min(320px, var(--full-width)));
|
||||
--width: min(var(--ha-dialog-width-sm, 320px), var(--full-width));
|
||||
}
|
||||
|
||||
:host([width="large"]) wa-dialog {
|
||||
--width: var(--ha-dialog-width-lg, min(720px, var(--full-width)));
|
||||
--width: min(var(--ha-dialog-width-lg, 720px), var(--full-width));
|
||||
}
|
||||
|
||||
:host([width="full"]) wa-dialog {
|
||||
@@ -211,6 +218,7 @@ export class HaWaDialog extends LitElement {
|
||||
--ha-dialog-max-height,
|
||||
calc(100% - var(--ha-space-20))
|
||||
);
|
||||
min-height: var(--ha-dialog-min-height);
|
||||
position: var(--dialog-surface-position, relative);
|
||||
margin-top: var(--dialog-surface-margin-top, auto);
|
||||
display: flex;
|
||||
@@ -247,10 +255,7 @@ export class HaWaDialog extends LitElement {
|
||||
.header-title {
|
||||
margin: 0;
|
||||
margin-bottom: 0;
|
||||
color: var(
|
||||
--ha-dialog-header-title-color,
|
||||
var(--ha-color-on-surface-default, var(--primary-text-color))
|
||||
);
|
||||
color: var(--ha-dialog-header-title-color, var(--primary-text-color));
|
||||
font-size: var(
|
||||
--ha-dialog-header-title-font-size,
|
||||
var(--ha-font-size-2xl)
|
||||
@@ -287,6 +292,7 @@ export class HaWaDialog extends LitElement {
|
||||
}
|
||||
:host([flexcontent]) .body {
|
||||
max-width: 100%;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@@ -315,6 +321,7 @@ declare global {
|
||||
|
||||
interface HASSDomEvents {
|
||||
opened: undefined;
|
||||
"after-show": undefined;
|
||||
closed: undefined;
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ export class HaTargetPickerItemGroup extends LitElement {
|
||||
Record<TargetType, string[]>
|
||||
>;
|
||||
|
||||
@property({ type: Boolean }) public collapsed = false;
|
||||
@property({ type: Boolean, reflect: true }) public collapsed = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
public deviceFilter?: HaDevicePickerDeviceFilterFunc;
|
||||
@@ -50,7 +50,11 @@ export class HaTargetPickerItemGroup extends LitElement {
|
||||
}
|
||||
});
|
||||
|
||||
return html`<ha-expansion-panel .expanded=${!this.collapsed} left-chevron>
|
||||
return html`<ha-expansion-panel
|
||||
.expanded=${!this.collapsed}
|
||||
left-chevron
|
||||
@expanded-changed=${this._expandedChanged}
|
||||
>
|
||||
<div slot="header" class="heading">
|
||||
${this.hass.localize(
|
||||
`ui.components.target-picker.selected.${this.type}`,
|
||||
@@ -78,6 +82,10 @@ export class HaTargetPickerItemGroup extends LitElement {
|
||||
</ha-expansion-panel>`;
|
||||
}
|
||||
|
||||
private _expandedChanged(ev: CustomEvent) {
|
||||
this.collapsed = !ev.detail.expanded;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
|
@@ -130,7 +130,7 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-md-list-item type="text">
|
||||
<div slot="start">
|
||||
<div class="icon" slot="start">
|
||||
${this.subEntry
|
||||
? html`
|
||||
<div class="horizontal-line-wrapper">
|
||||
@@ -172,7 +172,9 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
((entries && (showEntities || showDevices)) || this._domainName)
|
||||
? html`
|
||||
<div slot="end" class="summary">
|
||||
${showEntities && !this.expand
|
||||
${showEntities &&
|
||||
!this.expand &&
|
||||
entries?.referenced_entities.length
|
||||
? html`<button class="main link" @click=${this._openDetails}>
|
||||
${this.hass.localize(
|
||||
"ui.components.target-picker.entities_count",
|
||||
@@ -606,6 +608,11 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
state-badge {
|
||||
color: var(--ha-color-on-neutral-quiet);
|
||||
}
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
@@ -629,9 +636,6 @@ export class HaTargetPickerItemRow extends LitElement {
|
||||
font-size: var(--ha-font-size-s);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.domain {
|
||||
font-family: var(--ha-font-family-code);
|
||||
}
|
||||
|
||||
.entries-tree {
|
||||
display: flex;
|
||||
|
@@ -520,6 +520,7 @@ export class HaTargetPickerSelector extends LitElement {
|
||||
id=${`list-item-${index}`}
|
||||
tabindex="-1"
|
||||
.type=${type === "empty" ? "text" : "button"}
|
||||
class=${type === "empty" ? "empty" : ""}
|
||||
@click=${this._handlePickTarget}
|
||||
.targetType=${type}
|
||||
.targetId=${type !== "empty" ? item.id : undefined}
|
||||
@@ -574,9 +575,7 @@ export class HaTargetPickerSelector extends LitElement {
|
||||
})}
|
||||
/>
|
||||
`
|
||||
: type === "area" &&
|
||||
(item as FloorComboBoxItem).type === "floor" &&
|
||||
(item as FloorComboBoxItem).floor
|
||||
: type === "floor"
|
||||
? html`<ha-floor-icon
|
||||
slot="start"
|
||||
.floor=${(item as FloorComboBoxItem).floor!}
|
||||
@@ -836,7 +835,7 @@ export class HaTargetPickerSelector extends LitElement {
|
||||
id: EMPTY_SEARCH,
|
||||
primary: this.hass.localize(
|
||||
"ui.components.target-picker.no_target_found",
|
||||
{ term: html`<span class="search-term">"${searchTerm}"</span>` }
|
||||
{ term: html`<div><b>‘${searchTerm}’</b></div>` }
|
||||
),
|
||||
});
|
||||
} else if (items.length === 0) {
|
||||
@@ -969,6 +968,9 @@ export class HaTargetPickerSelector extends LitElement {
|
||||
private _onScrollList(ev) {
|
||||
const top = ev.target.scrollTop ?? 0;
|
||||
this._listScrolled = top > 0;
|
||||
if (this.mode === "dialog") {
|
||||
fireEvent(this, "bottom-sheet-lock-resize-changed", this._listScrolled);
|
||||
}
|
||||
}
|
||||
|
||||
private _resetSelectedItem() {
|
||||
@@ -1020,10 +1022,14 @@ export class HaTargetPickerSelector extends LitElement {
|
||||
padding: var(--ha-space-1) var(--ha-space-2);
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
color: var(--secondary-text-color);
|
||||
min-height: var(--ha-space-6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
min-height: var(--ha-space-8);
|
||||
}
|
||||
|
||||
:host([mode="dialog"]) .title {
|
||||
@@ -1055,7 +1061,6 @@ export class HaTargetPickerSelector extends LitElement {
|
||||
|
||||
.filter-header {
|
||||
opacity: 0;
|
||||
transition: opacity 300ms ease-in;
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
width: calc(100% - var(--ha-space-8));
|
||||
@@ -1083,9 +1088,8 @@ export class HaTargetPickerSelector extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-term {
|
||||
color: var(--primary-color);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
.empty {
|
||||
text-align: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -6,8 +6,6 @@ import {
|
||||
mdiCallSplit,
|
||||
mdiCodeBraces,
|
||||
mdiDevices,
|
||||
mdiDotsHorizontal,
|
||||
mdiExcavator,
|
||||
mdiFormatListNumbered,
|
||||
mdiGestureDoubleTap,
|
||||
mdiHandBackRight,
|
||||
@@ -16,10 +14,10 @@ import {
|
||||
mdiRoomService,
|
||||
mdiShuffleDisabled,
|
||||
mdiTimerOutline,
|
||||
mdiTools,
|
||||
mdiTrafficLight,
|
||||
} from "@mdi/js";
|
||||
import type { AutomationElementGroup } from "./automation";
|
||||
import type { AutomationElementGroupCollection } from "./automation";
|
||||
import type { Action } from "./script";
|
||||
|
||||
export const ACTION_ICONS = {
|
||||
condition: mdiAbTesting,
|
||||
@@ -48,37 +46,73 @@ export const YAML_ONLY_ACTION_TYPES = new Set<keyof typeof ACTION_ICONS>([
|
||||
"variables",
|
||||
]);
|
||||
|
||||
export const ACTION_GROUPS: AutomationElementGroup = {
|
||||
device_id: {},
|
||||
helpers: {
|
||||
icon: mdiTools,
|
||||
members: {},
|
||||
},
|
||||
building_blocks: {
|
||||
icon: mdiExcavator,
|
||||
members: {
|
||||
condition: {},
|
||||
delay: {},
|
||||
wait_template: {},
|
||||
wait_for_trigger: {},
|
||||
repeat_count: {},
|
||||
repeat_while: {},
|
||||
repeat_until: {},
|
||||
repeat_for_each: {},
|
||||
choose: {},
|
||||
if: {},
|
||||
stop: {},
|
||||
sequence: {},
|
||||
parallel: {},
|
||||
variables: {},
|
||||
export const ACTION_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
{
|
||||
groups: {
|
||||
device_id: {},
|
||||
serviceGroups: {},
|
||||
},
|
||||
},
|
||||
other: {
|
||||
icon: mdiDotsHorizontal,
|
||||
members: {
|
||||
{
|
||||
titleKey: "ui.panel.config.automation.editor.actions.groups.helpers.label",
|
||||
groups: {
|
||||
helpers: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
titleKey: "ui.panel.config.automation.editor.actions.groups.other.label",
|
||||
groups: {
|
||||
event: {},
|
||||
service: {},
|
||||
set_conversation_response: {},
|
||||
other: {},
|
||||
},
|
||||
},
|
||||
] as const;
|
||||
|
||||
export const ACTION_BUILDING_BLOCKS_GROUP = {
|
||||
condition: {},
|
||||
delay: {},
|
||||
wait_template: {},
|
||||
wait_for_trigger: {},
|
||||
repeat_count: {},
|
||||
repeat_while: {},
|
||||
repeat_until: {},
|
||||
repeat_for_each: {},
|
||||
choose: {},
|
||||
if: {},
|
||||
stop: {},
|
||||
sequence: {},
|
||||
parallel: {},
|
||||
variables: {},
|
||||
};
|
||||
|
||||
// These will be replaced with the correct action
|
||||
export const VIRTUAL_ACTIONS: Partial<
|
||||
Record<keyof typeof ACTION_BUILDING_BLOCKS_GROUP, Action>
|
||||
> = {
|
||||
repeat_count: {
|
||||
repeat: {
|
||||
count: 2,
|
||||
sequence: [],
|
||||
},
|
||||
},
|
||||
repeat_while: {
|
||||
repeat: {
|
||||
while: [],
|
||||
sequence: [],
|
||||
},
|
||||
},
|
||||
repeat_until: {
|
||||
repeat: {
|
||||
until: [],
|
||||
sequence: [],
|
||||
},
|
||||
},
|
||||
repeat_for_each: {
|
||||
repeat: {
|
||||
for_each: {},
|
||||
sequence: [],
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
@@ -218,6 +218,7 @@ export const getAreasAndFloors = (
|
||||
type: "floor",
|
||||
primary: floorName,
|
||||
floor: floor,
|
||||
icon: floor.icon || undefined,
|
||||
search_labels: [
|
||||
floor.floor_id,
|
||||
floorName,
|
||||
|
@@ -4,6 +4,7 @@ import type {
|
||||
} from "home-assistant-js-websocket";
|
||||
import { ensureArray } from "../common/array/ensure-array";
|
||||
import { navigate } from "../common/navigate";
|
||||
import type { LocalizeKeys } from "../common/translations/localize";
|
||||
import { createSearchParam } from "../common/url/search-params";
|
||||
import type { Context, HomeAssistant } from "../types";
|
||||
import type { BlueprintInput } from "./blueprint";
|
||||
@@ -293,6 +294,11 @@ export interface ShorthandNotCondition extends ShorthandBaseCondition {
|
||||
not: Condition[];
|
||||
}
|
||||
|
||||
export interface AutomationElementGroupCollection {
|
||||
titleKey?: LocalizeKeys;
|
||||
groups: AutomationElementGroup;
|
||||
}
|
||||
|
||||
export type AutomationElementGroup = Record<
|
||||
string,
|
||||
{ icon?: string; members?: AutomationElementGroup }
|
||||
|
@@ -3,8 +3,6 @@ import {
|
||||
mdiClockOutline,
|
||||
mdiCodeBraces,
|
||||
mdiDevices,
|
||||
mdiDotsHorizontal,
|
||||
mdiExcavator,
|
||||
mdiGateOr,
|
||||
mdiIdentifier,
|
||||
mdiMapClock,
|
||||
@@ -15,7 +13,7 @@ import {
|
||||
mdiStateMachine,
|
||||
mdiWeatherSunny,
|
||||
} from "@mdi/js";
|
||||
import type { AutomationElementGroup } from "./automation";
|
||||
import type { AutomationElementGroupCollection } from "./automation";
|
||||
|
||||
export const CONDITION_ICONS = {
|
||||
device: mdiDevices,
|
||||
@@ -31,25 +29,31 @@ export const CONDITION_ICONS = {
|
||||
zone: mdiMapMarkerRadius,
|
||||
};
|
||||
|
||||
export const CONDITION_GROUPS: AutomationElementGroup = {
|
||||
device: {},
|
||||
entity: { icon: mdiShape, members: { state: {}, numeric_state: {} } },
|
||||
time_location: {
|
||||
icon: mdiMapClock,
|
||||
members: { sun: {}, time: {}, zone: {} },
|
||||
export const CONDITION_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
{
|
||||
groups: {
|
||||
device: {},
|
||||
entity: { icon: mdiShape, members: { state: {}, numeric_state: {} } },
|
||||
time_location: {
|
||||
icon: mdiMapClock,
|
||||
members: { sun: {}, time: {}, zone: {} },
|
||||
},
|
||||
},
|
||||
},
|
||||
building_blocks: {
|
||||
icon: mdiExcavator,
|
||||
members: { and: {}, or: {}, not: {} },
|
||||
},
|
||||
other: {
|
||||
icon: mdiDotsHorizontal,
|
||||
members: {
|
||||
{
|
||||
titleKey: "ui.panel.config.automation.editor.conditions.groups.other.label",
|
||||
groups: {
|
||||
template: {},
|
||||
trigger: {},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
] as const;
|
||||
|
||||
export const CONDITION_BUILDING_BLOCKS_GROUP = {
|
||||
and: {},
|
||||
or: {},
|
||||
not: {},
|
||||
};
|
||||
|
||||
export const CONDITION_BUILDING_BLOCKS = ["and", "or", "not"];
|
||||
|
||||
|
@@ -4,7 +4,6 @@ import {
|
||||
mdiClockOutline,
|
||||
mdiCodeBraces,
|
||||
mdiDevices,
|
||||
mdiDotsHorizontal,
|
||||
mdiFormatListBulleted,
|
||||
mdiGestureDoubleTap,
|
||||
mdiMapClock,
|
||||
@@ -23,7 +22,7 @@ import {
|
||||
|
||||
import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg";
|
||||
import type {
|
||||
AutomationElementGroup,
|
||||
AutomationElementGroupCollection,
|
||||
Trigger,
|
||||
TriggerList,
|
||||
} from "./automation";
|
||||
@@ -49,16 +48,26 @@ export const TRIGGER_ICONS = {
|
||||
list: mdiFormatListBulleted,
|
||||
};
|
||||
|
||||
export const TRIGGER_GROUPS: AutomationElementGroup = {
|
||||
device: {},
|
||||
entity: { icon: mdiShape, members: { state: {}, numeric_state: {} } },
|
||||
time_location: {
|
||||
icon: mdiMapClock,
|
||||
members: { calendar: {}, sun: {}, time: {}, time_pattern: {}, zone: {} },
|
||||
export const TRIGGER_COLLECTIONS: AutomationElementGroupCollection[] = [
|
||||
{
|
||||
groups: {
|
||||
device: {},
|
||||
entity: { icon: mdiShape, members: { state: {}, numeric_state: {} } },
|
||||
time_location: {
|
||||
icon: mdiMapClock,
|
||||
members: {
|
||||
calendar: {},
|
||||
sun: {},
|
||||
time: {},
|
||||
time_pattern: {},
|
||||
zone: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
other: {
|
||||
icon: mdiDotsHorizontal,
|
||||
members: {
|
||||
{
|
||||
titleKey: "ui.panel.config.automation.editor.triggers.groups.other.label",
|
||||
groups: {
|
||||
event: {},
|
||||
geo_location: {},
|
||||
homeassistant: {},
|
||||
@@ -70,7 +79,7 @@ export const TRIGGER_GROUPS: AutomationElementGroup = {
|
||||
persistent_notification: {},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
] as const;
|
||||
|
||||
export const isTriggerList = (trigger: Trigger): trigger is TriggerList =>
|
||||
"triggers" in trigger;
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant, mdiPlus } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, queryAll, state } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||
import { storage } from "../../../../common/decorators/storage";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||
@@ -15,19 +16,18 @@ import {
|
||||
ACTION_BUILDING_BLOCKS,
|
||||
getService,
|
||||
isService,
|
||||
VIRTUAL_ACTIONS,
|
||||
} from "../../../../data/action";
|
||||
import type { AutomationClipboard } from "../../../../data/automation";
|
||||
import type { Action } from "../../../../data/script";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import {
|
||||
PASTE_VALUE,
|
||||
VIRTUAL_ACTIONS,
|
||||
showAddAutomationElementDialog,
|
||||
} from "../show-add-automation-element-dialog";
|
||||
import { automationRowsStyles } from "../styles";
|
||||
import type HaAutomationActionRow from "./ha-automation-action-row";
|
||||
import { getAutomationActionType } from "./ha-automation-action-row";
|
||||
import { ensureArray } from "../../../../common/array/ensure-array";
|
||||
|
||||
@customElement("ha-automation-action")
|
||||
export default class HaAutomationAction extends LitElement {
|
||||
@@ -115,7 +115,9 @@ export default class HaAutomationAction extends LitElement {
|
||||
@click=${stopPropagation}
|
||||
.index=${idx}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
@@ -134,17 +136,6 @@ export default class HaAutomationAction extends LitElement {
|
||||
"ui.panel.config.automation.editor.actions.add"
|
||||
)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
.disabled=${this.disabled}
|
||||
@click=${this._addActionBuildingBlockDialog}
|
||||
appearance="plain"
|
||||
.size=${this.root ? "medium" : "small"}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.actions.add_building_block"
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</ha-sortable>
|
||||
@@ -220,15 +211,6 @@ export default class HaAutomationAction extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _addActionBuildingBlockDialog() {
|
||||
showAddAutomationElementDialog(this, {
|
||||
type: "action",
|
||||
add: this._addAction,
|
||||
clipboardItem: getAutomationActionType(this._clipboard?.action),
|
||||
group: "building_blocks",
|
||||
});
|
||||
}
|
||||
|
||||
private _addAction = (action: string) => {
|
||||
let actions: Action[];
|
||||
if (action === PASTE_VALUE) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant, mdiPlus } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
@@ -193,7 +193,9 @@ export default class HaAutomationCondition extends LitElement {
|
||||
@click=${stopPropagation}
|
||||
.index=${idx}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
@@ -212,17 +214,6 @@ export default class HaAutomationCondition extends LitElement {
|
||||
"ui.panel.config.automation.editor.conditions.add"
|
||||
)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
.disabled=${this.disabled}
|
||||
appearance="plain"
|
||||
.size=${this.root ? "medium" : "small"}
|
||||
@click=${this._addConditionBuildingBlockDialog}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiPlus} slot="start"></ha-svg-icon>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.editor.conditions.add_building_block"
|
||||
)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</ha-sortable>
|
||||
@@ -240,15 +231,6 @@ export default class HaAutomationCondition extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _addConditionBuildingBlockDialog() {
|
||||
showAddAutomationElementDialog(this, {
|
||||
type: "condition",
|
||||
add: this._addCondition,
|
||||
clipboardItem: this._clipboard?.condition?.condition,
|
||||
group: "building_blocks",
|
||||
});
|
||||
}
|
||||
|
||||
private _addCondition = (value) => {
|
||||
let conditions: Condition[];
|
||||
if (value === PASTE_VALUE) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant, mdiPlus } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
@@ -100,7 +100,9 @@ export default class HaAutomationOption extends LitElement {
|
||||
@click=${stopPropagation}
|
||||
.index=${idx}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
|
@@ -1,45 +1,11 @@
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import type { ACTION_GROUPS } from "../../../data/action";
|
||||
import type { ActionType } from "../../../data/script";
|
||||
|
||||
export const PASTE_VALUE = "__paste__";
|
||||
|
||||
// These will be replaced with the correct action
|
||||
export const VIRTUAL_ACTIONS: Record<
|
||||
keyof (typeof ACTION_GROUPS)["building_blocks"]["members"],
|
||||
ActionType
|
||||
> = {
|
||||
repeat_count: {
|
||||
repeat: {
|
||||
count: 2,
|
||||
sequence: [],
|
||||
},
|
||||
},
|
||||
repeat_while: {
|
||||
repeat: {
|
||||
while: [],
|
||||
sequence: [],
|
||||
},
|
||||
},
|
||||
repeat_until: {
|
||||
repeat: {
|
||||
until: [],
|
||||
sequence: [],
|
||||
},
|
||||
},
|
||||
repeat_for_each: {
|
||||
repeat: {
|
||||
for_each: {},
|
||||
sequence: [],
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export interface AddAutomationElementDialogParams {
|
||||
type: "trigger" | "condition" | "action";
|
||||
add: (key: string) => void;
|
||||
clipboardItem: string | undefined;
|
||||
group?: string;
|
||||
}
|
||||
const loadDialog = () => import("./add-automation-element-dialog");
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiDrag, mdiPlus } from "@mdi/js";
|
||||
import { mdiDragHorizontalVariant, mdiPlus } from "@mdi/js";
|
||||
import deepClone from "deep-clone-simple";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
@@ -110,7 +110,9 @@ export default class HaAutomationTrigger extends LitElement {
|
||||
@click=${stopPropagation}
|
||||
.index=${idx}
|
||||
>
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
|
@@ -244,7 +244,8 @@ class HaConfigBackupSettings extends LitElement {
|
||||
`
|
||||
: nothing}
|
||||
</div>
|
||||
${!this.cloudStatus?.logged_in
|
||||
${!this.cloudStatus?.logged_in &&
|
||||
isComponentLoaded(this.hass, "cloud")
|
||||
? html`<ha-card class="cloud-info">
|
||||
<div class="cloud-header">
|
||||
<img
|
||||
@@ -279,7 +280,10 @@ class HaConfigBackupSettings extends LitElement {
|
||||
"ui.panel.config.voice_assistants.assistants.cloud.sign_in"
|
||||
)}
|
||||
</ha-button>
|
||||
<ha-button href="/config/cloud/register">
|
||||
<ha-button
|
||||
href="/config/cloud/register"
|
||||
appearance="filled"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.voice_assistants.assistants.cloud.try_one_month"
|
||||
)}
|
||||
|
@@ -1,4 +1,10 @@
|
||||
import { mdiDelete, mdiDevices, mdiDrag, mdiPencil, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
mdiDelete,
|
||||
mdiDevices,
|
||||
mdiDragHorizontalVariant,
|
||||
mdiPencil,
|
||||
mdiPlus,
|
||||
} from "@mdi/js";
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
@@ -89,7 +95,9 @@ export class EnergyDeviceSettings extends LitElement {
|
||||
(device) => html`
|
||||
<div class="row" .device=${device}>
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
<span class="content"
|
||||
>${device.name ||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiDelete, mdiDrag } from "@mdi/js";
|
||||
import { mdiDelete, mdiDragHorizontalVariant } from "@mdi/js";
|
||||
import type { CSSResultGroup } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@@ -111,7 +111,9 @@ class HaInputSelectForm extends LitElement {
|
||||
<ha-list-item class="option" hasMeta>
|
||||
<div class="optioncontent">
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
${option}
|
||||
</div>
|
||||
|
@@ -9,7 +9,6 @@ import {
|
||||
import type { PropertyValues } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import memoize from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../../common/config/is_component_loaded";
|
||||
import { storage } from "../../../../common/decorators/storage";
|
||||
@@ -62,7 +61,7 @@ type DataTableItem = Pick<
|
||||
> & {
|
||||
default: boolean;
|
||||
filename: string;
|
||||
iconColor?: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
@customElement("ha-config-lovelace-dashboards")
|
||||
@@ -107,6 +106,20 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
})
|
||||
private _activeHiddenColumns?: string[];
|
||||
|
||||
@storage({
|
||||
key: "lovelace-dashboards-table-grouping",
|
||||
state: false,
|
||||
subscribe: false,
|
||||
})
|
||||
private _activeGrouping?: string = "type";
|
||||
|
||||
@storage({
|
||||
key: "lovelace-dashboards-table-collapsed",
|
||||
state: false,
|
||||
subscribe: false,
|
||||
})
|
||||
private _activeCollapsed: string[] = [];
|
||||
|
||||
public willUpdate() {
|
||||
if (!this.hasUpdated) {
|
||||
this.hass.loadFragmentTranslation("lovelace");
|
||||
@@ -132,15 +145,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
template: (dashboard) =>
|
||||
dashboard.icon
|
||||
? html`
|
||||
<ha-icon
|
||||
slot="item-icon"
|
||||
.icon=${dashboard.icon}
|
||||
style=${ifDefined(
|
||||
dashboard.iconColor
|
||||
? `color: ${dashboard.iconColor}`
|
||||
: undefined
|
||||
)}
|
||||
></ha-icon>
|
||||
<ha-icon slot="item-icon" .icon=${dashboard.icon}></ha-icon>
|
||||
`
|
||||
: nothing,
|
||||
},
|
||||
@@ -177,6 +182,15 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
},
|
||||
};
|
||||
|
||||
columns.type = {
|
||||
title: localize(
|
||||
"ui.panel.config.lovelace.dashboards.picker.headers.type"
|
||||
),
|
||||
sortable: true,
|
||||
groupable: true,
|
||||
filterable: true,
|
||||
};
|
||||
|
||||
columns.mode = {
|
||||
title: localize(
|
||||
"ui.panel.config.lovelace.dashboards.picker.headers.conf_mode"
|
||||
@@ -287,7 +301,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
url_path: "lovelace",
|
||||
mode: defaultMode,
|
||||
filename: defaultMode === "yaml" ? "ui-lovelace.yaml" : "",
|
||||
iconColor: "var(--primary-color)",
|
||||
type: this._localizeType("built_in"),
|
||||
},
|
||||
];
|
||||
if (isComponentLoaded(this.hass, "energy")) {
|
||||
@@ -298,9 +312,9 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
mode: "storage",
|
||||
url_path: "energy",
|
||||
filename: "",
|
||||
iconColor: "var(--orange-color)",
|
||||
default: false,
|
||||
require_admin: false,
|
||||
type: this._localizeType("built_in"),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -312,9 +326,9 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
mode: "storage",
|
||||
url_path: "light",
|
||||
filename: "",
|
||||
iconColor: "var(--amber-color)",
|
||||
default: false,
|
||||
require_admin: false,
|
||||
type: this._localizeType("built_in"),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,9 +340,9 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
mode: "storage",
|
||||
url_path: "safety",
|
||||
filename: "",
|
||||
iconColor: "var(--blue-grey-color)",
|
||||
default: false,
|
||||
require_admin: false,
|
||||
type: this._localizeType("built_in"),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -340,9 +354,9 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
mode: "storage",
|
||||
url_path: "climate",
|
||||
filename: "",
|
||||
iconColor: "var(--deep-orange-color)",
|
||||
default: false,
|
||||
require_admin: false,
|
||||
type: this._localizeType("built_in"),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -351,16 +365,25 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
.sort((a, b) =>
|
||||
stringCompare(a.title, b.title, this.hass.locale.language)
|
||||
)
|
||||
.map((dashboard) => ({
|
||||
filename: "",
|
||||
...dashboard,
|
||||
default: defaultUrlPath === dashboard.url_path,
|
||||
}))
|
||||
.map(
|
||||
(dashboard) =>
|
||||
({
|
||||
filename: "",
|
||||
...dashboard,
|
||||
default: defaultUrlPath === dashboard.url_path,
|
||||
type: this._localizeType("user_created"),
|
||||
}) satisfies DataTableItem
|
||||
)
|
||||
);
|
||||
return result;
|
||||
}
|
||||
);
|
||||
|
||||
private _localizeType = (type: "user_created" | "built_in") =>
|
||||
this.hass.localize(
|
||||
`ui.panel.config.lovelace.dashboards.picker.type.${type}`
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || this._dashboards === undefined) {
|
||||
return html` <hass-loading-screen></hass-loading-screen> `;
|
||||
@@ -380,9 +403,13 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
this.hass.localize
|
||||
)}
|
||||
.data=${this._getItems(this._dashboards, this.hass.defaultPanel)}
|
||||
.initialGroupColumn=${this._activeGrouping}
|
||||
.initialCollapsedGroups=${this._activeCollapsed}
|
||||
.initialSorting=${this._activeSorting}
|
||||
.columnOrder=${this._activeColumnOrder}
|
||||
.hiddenColumns=${this._activeHiddenColumns}
|
||||
@grouping-changed=${this._handleGroupingChanged}
|
||||
@collapsed-changed=${this._handleCollapseChanged}
|
||||
@columns-changed=${this._handleColumnsChanged}
|
||||
@sorting-changed=${this._handleSortingChanged}
|
||||
.filter=${this._filter}
|
||||
@@ -443,13 +470,13 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
}
|
||||
|
||||
private _canDelete(urlPath: string) {
|
||||
return !["lovelace", "energy", "light", "security", "climate"].includes(
|
||||
return !["lovelace", "energy", "light", "safety", "climate"].includes(
|
||||
urlPath
|
||||
);
|
||||
}
|
||||
|
||||
private _canEdit(urlPath: string) {
|
||||
return !["light", "security", "climate"].includes(urlPath);
|
||||
return !["light", "safety", "climate"].includes(urlPath);
|
||||
}
|
||||
|
||||
private _handleDelete = async (item: DataTableItem) => {
|
||||
@@ -571,6 +598,14 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
||||
this._activeColumnOrder = ev.detail.columnOrder;
|
||||
this._activeHiddenColumns = ev.detail.hiddenColumns;
|
||||
}
|
||||
|
||||
private _handleGroupingChanged(ev: CustomEvent) {
|
||||
this._activeGrouping = ev.detail.value;
|
||||
}
|
||||
|
||||
private _handleCollapseChanged(ev: CustomEvent) {
|
||||
this._activeCollapsed = ev.detail.value;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@@ -11,7 +11,6 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
|
||||
import "../../../components/ha-badge";
|
||||
import "../../../components/ha-ripple";
|
||||
import "../../../components/ha-state-icon";
|
||||
@@ -20,6 +19,7 @@ import { cameraUrlWithWidthHeight } from "../../../data/camera";
|
||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
|
||||
import { findEntities } from "../common/find-entities";
|
||||
import { handleAction } from "../common/handle-action";
|
||||
import { hasAction } from "../common/has-action";
|
||||
@@ -162,11 +162,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
||||
if (!stateObj) {
|
||||
return html`
|
||||
<ha-badge .label=${entityId} class="error">
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.hass=${this.hass}
|
||||
.path=${mdiAlertCircle}
|
||||
></ha-svg-icon>
|
||||
<ha-svg-icon slot="icon" .path=${mdiAlertCircle}></ha-svg-icon>
|
||||
${this.hass.localize("ui.badge.entity.not_found")}
|
||||
</ha-badge>
|
||||
`;
|
||||
@@ -179,22 +175,22 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
||||
"--badge-color": color,
|
||||
};
|
||||
|
||||
const stateDisplay = html`
|
||||
<state-display
|
||||
.stateObj=${stateObj}
|
||||
.hass=${this.hass}
|
||||
.content=${this._config.state_content}
|
||||
.name=${this._config.name}
|
||||
>
|
||||
</state-display>
|
||||
`;
|
||||
|
||||
const name = computeLovelaceEntityName(
|
||||
this.hass,
|
||||
stateObj,
|
||||
this._config.name
|
||||
);
|
||||
|
||||
const stateDisplay = html`
|
||||
<state-display
|
||||
.stateObj=${stateObj}
|
||||
.hass=${this.hass}
|
||||
.content=${this._config.state_content}
|
||||
.name=${name}
|
||||
>
|
||||
</state-display>
|
||||
`;
|
||||
|
||||
const showState = this._config.show_state;
|
||||
const showName = this._config.show_name;
|
||||
const showIcon = this._config.show_icon;
|
||||
|
@@ -5,7 +5,7 @@ import {
|
||||
mdiDelete,
|
||||
mdiDeleteSweep,
|
||||
mdiDotsVertical,
|
||||
mdiDrag,
|
||||
mdiDragHorizontalVariant,
|
||||
mdiPlus,
|
||||
mdiSort,
|
||||
} from "@mdi/js";
|
||||
@@ -522,7 +522,7 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard {
|
||||
"ui.panel.lovelace.cards.todo-list.drag_and_drop"
|
||||
)}
|
||||
class="reorderButton handle"
|
||||
.path=${mdiDrag}
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
slot="meta"
|
||||
>
|
||||
</ha-svg-icon>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiClose, mdiDrag, mdiPencil } from "@mdi/js";
|
||||
import { mdiClose, mdiDragHorizontalVariant, mdiPencil } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
@@ -66,7 +66,11 @@ export class HuiEntityEditor extends LitElement {
|
||||
|
||||
return html`
|
||||
<ha-md-list-item class="item">
|
||||
<ha-svg-icon class="handle" .path=${mdiDrag} slot="start"></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
class="handle"
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
slot="start"
|
||||
></ha-svg-icon>
|
||||
|
||||
<div slot="headline" class="label">${primary}</div>
|
||||
${secondary
|
||||
@@ -152,7 +156,9 @@ export class HuiEntityEditor extends LitElement {
|
||||
(entityConf, index) => html`
|
||||
<div class="entity" data-entity-id=${entityConf.entity}>
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
<ha-entity-picker
|
||||
.hass=${this.hass}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiDelete, mdiDrag, mdiPencil } from "@mdi/js";
|
||||
import { mdiDelete, mdiDragHorizontalVariant, mdiPencil } from "@mdi/js";
|
||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
@@ -31,7 +31,7 @@ export class HuiSectionEditMode extends LitElement {
|
||||
<ha-svg-icon
|
||||
aria-hidden="true"
|
||||
class="handle"
|
||||
.path=${mdiDrag}
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize("ui.common.edit")}
|
||||
|
@@ -1,4 +1,9 @@
|
||||
import { mdiDelete, mdiDrag, mdiPencil, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
mdiDelete,
|
||||
mdiDragHorizontalVariant,
|
||||
mdiPencil,
|
||||
mdiPlus,
|
||||
} from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
@@ -345,7 +350,9 @@ export class HuiCardFeaturesEditor extends LitElement {
|
||||
return html`
|
||||
<div class="feature">
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
<div class="feature-content">
|
||||
<div>
|
||||
|
@@ -1,5 +1,10 @@
|
||||
import "@material/mwc-menu/mwc-menu-surface";
|
||||
import { mdiDelete, mdiDrag, mdiPencil, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
mdiDelete,
|
||||
mdiDragHorizontalVariant,
|
||||
mdiPencil,
|
||||
mdiPlus,
|
||||
} from "@mdi/js";
|
||||
import type { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
@@ -86,7 +91,9 @@ export class HuiHeadingBadgesEditor extends LitElement {
|
||||
return html`
|
||||
<div class="badge">
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon
|
||||
.path=${mdiDragHorizontalVariant}
|
||||
></ha-svg-icon>
|
||||
</div>
|
||||
<div class="badge-content">
|
||||
<span>${label}</span>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { mdiClose, mdiDrag, mdiPencil } from "@mdi/js";
|
||||
import { mdiClose, mdiDragHorizontalVariant, mdiPencil } from "@mdi/js";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
@@ -59,7 +59,7 @@ export class HuiEntitiesCardRowEditor extends LitElement {
|
||||
(entityConf, index) => html`
|
||||
<div class="entity">
|
||||
<div class="handle">
|
||||
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||
<ha-svg-icon .path=${mdiDragHorizontalVariant}></ha-svg-icon>
|
||||
</div>
|
||||
${entityConf.type
|
||||
? html`
|
||||
|
@@ -1,8 +1,12 @@
|
||||
import { css, LitElement, nothing } from "lit";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceViewBackgroundConfig } from "../../../data/lovelace/config/view";
|
||||
import {
|
||||
isMediaSourceContentId,
|
||||
resolveMediaSource,
|
||||
} from "../../../data/media_source";
|
||||
|
||||
@customElement("hui-view-background")
|
||||
export class HUIViewBackground extends LitElement {
|
||||
@@ -13,10 +17,27 @@ export class HUIViewBackground extends LitElement {
|
||||
| LovelaceViewBackgroundConfig
|
||||
| undefined;
|
||||
|
||||
@state({ attribute: false }) resolvedImage?: string;
|
||||
|
||||
protected render() {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
private _fetchMedia() {
|
||||
const backgroundImage =
|
||||
typeof this.background === "string"
|
||||
? this.background
|
||||
: this.background?.image;
|
||||
|
||||
if (backgroundImage && isMediaSourceContentId(backgroundImage)) {
|
||||
resolveMediaSource(this.hass, backgroundImage).then((result) => {
|
||||
this.resolvedImage = result.url;
|
||||
});
|
||||
} else {
|
||||
this.resolvedImage = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _applyTheme() {
|
||||
const computedStyles = getComputedStyle(this);
|
||||
const themeBackground = computedStyles.getPropertyValue(
|
||||
@@ -52,13 +73,19 @@ export class HUIViewBackground extends LitElement {
|
||||
background?: string | LovelaceViewBackgroundConfig
|
||||
) {
|
||||
if (typeof background === "object" && background.image) {
|
||||
if (isMediaSourceContentId(background.image) && !this.resolvedImage) {
|
||||
return null;
|
||||
}
|
||||
const alignment = background.alignment ?? "center";
|
||||
const size = background.size ?? "cover";
|
||||
const repeat = background.repeat ?? "no-repeat";
|
||||
return `${alignment} / ${size} ${repeat} url('${this.hass.hassUrl(background.image)}')`;
|
||||
return `${alignment} / ${size} ${repeat} url('${this.hass.hassUrl(this.resolvedImage || background.image)}')`;
|
||||
}
|
||||
if (typeof background === "string") {
|
||||
return background;
|
||||
if (isMediaSourceContentId(background) && !this.resolvedImage) {
|
||||
return null;
|
||||
}
|
||||
return this.resolvedImage || background;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -90,6 +117,10 @@ export class HUIViewBackground extends LitElement {
|
||||
|
||||
if (changedProperties.has("background")) {
|
||||
this._applyTheme();
|
||||
this._fetchMedia();
|
||||
}
|
||||
if (changedProperties.has("resolvedImage")) {
|
||||
this._applyTheme();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -155,7 +155,6 @@ export const semanticColorStyles = css`
|
||||
|
||||
/* Surfaces */
|
||||
--ha-color-surface-default: var(--ha-color-neutral-95);
|
||||
--ha-color-on-surface-default: var(--ha-color-neutral-05);
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -287,6 +286,5 @@ export const darkSemanticColorStyles = css`
|
||||
|
||||
/* Surfaces */
|
||||
--ha-color-surface-default: var(--ha-color-neutral-10);
|
||||
--ha-color-on-surface-default: var(--ha-color-neutral-95);
|
||||
}
|
||||
`;
|
||||
|
@@ -3455,12 +3455,17 @@
|
||||
"require_admin": "Admin only",
|
||||
"sidebar": "In sidebar",
|
||||
"filename": "Filename",
|
||||
"url": "Open"
|
||||
"url": "Open",
|
||||
"type": "Type"
|
||||
},
|
||||
"open": "Open",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"add_dashboard": "Add dashboard"
|
||||
"add_dashboard": "Add dashboard",
|
||||
"type": {
|
||||
"user_created": "User created",
|
||||
"built_in": "Built-in"
|
||||
}
|
||||
},
|
||||
"confirm_delete_title": "Delete {dashboard_title}?",
|
||||
"confirm_delete_text": "This dashboard will be permanently deleted.",
|
||||
@@ -3910,7 +3915,6 @@
|
||||
"edit_yaml": "Edit in YAML",
|
||||
"edit_ui": "Edit in visual editor",
|
||||
"copy_to_clipboard": "Copy to clipboard",
|
||||
"search_in": "Search · {group}",
|
||||
"unknown_entity": "unknown entity",
|
||||
"edit_unknown_device": "Editor not available for unknown device",
|
||||
"switch_ui_yaml_error": "There are currently YAML errors in the automation, and it cannot be parsed. Switching to UI mode may cause pending changes to be lost. Press cancel to correct any errors before proceeding to prevent loss of pending changes, or continue if you are sure.",
|
||||
@@ -3923,6 +3927,7 @@
|
||||
"item_pasted": "{item} pasted",
|
||||
"ctrl": "Ctrl",
|
||||
"del": "Del",
|
||||
"blocks": "Blocks",
|
||||
"triggers": {
|
||||
"name": "Triggers",
|
||||
"header": "When",
|
||||
@@ -3930,7 +3935,7 @@
|
||||
"learn_more": "Learn more about triggers",
|
||||
"triggered": "Triggered",
|
||||
"add": "Add trigger",
|
||||
"search": "Search trigger",
|
||||
"empty_search": "No triggers found for {term}",
|
||||
"id": "Trigger ID",
|
||||
"id_helper": "Helps identify each run based on which trigger fired.",
|
||||
"optional": "Optional",
|
||||
@@ -3951,14 +3956,16 @@
|
||||
"trigger": "Trigger",
|
||||
"copied_to_clipboard": "Trigger copied to clipboard",
|
||||
"cut_to_clipboard": "Trigger cut to clipboard",
|
||||
"select": "Select a trigger",
|
||||
"groups": {
|
||||
"device": {
|
||||
"label": "Device"
|
||||
},
|
||||
"entity": {
|
||||
"label": "Entity",
|
||||
"description": "When something happens to an entity."
|
||||
"label": "Entity"
|
||||
},
|
||||
"time_location": {
|
||||
"label": "Time and location",
|
||||
"description": "When someone enters or leaves a zone, or at a specific time."
|
||||
"label": "Time and location"
|
||||
},
|
||||
"other": {
|
||||
"label": "Other triggers"
|
||||
@@ -4191,7 +4198,7 @@
|
||||
"description": "All conditions added here need to be satisfied for the automation to run. A condition can be satisfied or not at any given time, for example: ''If {user} is home''. You can use building blocks to create more complex conditions.",
|
||||
"learn_more": "Learn more about conditions",
|
||||
"add": "Add condition",
|
||||
"search": "Search condition",
|
||||
"empty_search": "No conditions and blocks found for {term}",
|
||||
"add_building_block": "Add building block",
|
||||
"test": "Test",
|
||||
"testing_error": "Condition did not pass",
|
||||
@@ -4213,21 +4220,22 @@
|
||||
"condition": "Condition",
|
||||
"copied_to_clipboard": "Condition copied to clipboard",
|
||||
"cut_to_clipboard": "Condition cut to clipboard",
|
||||
"select": "Select a condition",
|
||||
"groups": {
|
||||
"device": {
|
||||
"label": "Device"
|
||||
},
|
||||
"entity": {
|
||||
"label": "Entity",
|
||||
"description": "If an entity is in a specific state."
|
||||
"label": "Entity"
|
||||
},
|
||||
"time_location": {
|
||||
"label": "Time and location",
|
||||
"description": "If someone is in a zone or if the current time is before or after a specified time."
|
||||
"label": "Time and location"
|
||||
},
|
||||
"other": {
|
||||
"label": "Other conditions"
|
||||
},
|
||||
"building_blocks": {
|
||||
"label": "Building blocks",
|
||||
"description": "Build more complex conditions."
|
||||
"label": "Building blocks"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
@@ -4358,7 +4366,7 @@
|
||||
"description": "All actions added here will be performed in sequence when the automation runs. An action usually controls one of your areas, devices, or entities, for example: 'Turn on the lights'. You can use building blocks to create more complex sequences of actions.",
|
||||
"learn_more": "Learn more about actions",
|
||||
"add": "Add action",
|
||||
"search": "Search action",
|
||||
"empty_search": "No actions and blocks found for {term}",
|
||||
"add_building_block": "Add building block",
|
||||
"invalid_action": "Invalid action",
|
||||
"run": "Run action",
|
||||
@@ -4382,7 +4390,11 @@
|
||||
"action": "Action",
|
||||
"copied_to_clipboard": "Action copied to clipboard",
|
||||
"cut_to_clipboard": "Action cut to clipboard",
|
||||
"select": "Select an action",
|
||||
"groups": {
|
||||
"device_id": {
|
||||
"label": "Device"
|
||||
},
|
||||
"helpers": {
|
||||
"label": "Helpers"
|
||||
},
|
||||
@@ -4390,8 +4402,7 @@
|
||||
"label": "Other actions"
|
||||
},
|
||||
"building_blocks": {
|
||||
"label": "Building blocks",
|
||||
"description": "Build more complex sequences of actions."
|
||||
"label": "Building blocks"
|
||||
}
|
||||
},
|
||||
"type": {
|
||||
|
20
yarn.lock
20
yarn.lock
@@ -1284,15 +1284,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@codemirror/view@npm:6.38.5, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0":
|
||||
version: 6.38.5
|
||||
resolution: "@codemirror/view@npm:6.38.5"
|
||||
"@codemirror/view@npm:6.38.6, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0":
|
||||
version: 6.38.6
|
||||
resolution: "@codemirror/view@npm:6.38.6"
|
||||
dependencies:
|
||||
"@codemirror/state": "npm:^6.5.0"
|
||||
crelt: "npm:^1.0.6"
|
||||
style-mod: "npm:^4.1.0"
|
||||
w3c-keyname: "npm:^2.2.4"
|
||||
checksum: 10/2335b593770042eb3adfe369073432b07cd2d15f1e230ae4dc7be7a7b8edd74e57c13e59b92a11e7e5d59ae030aabf7f55478dfec1cf2a2fe3a1ef3f091676a4
|
||||
checksum: 10/5a047337a98de111817ce8c8d39e6429c90ca0b0a4d2678d6e161e9e5961b1d476a891f447ab7a05cac395d4a93530e7c68bedd93191285265f0742a308ad00b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1942,9 +1942,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@home-assistant/webawesome@npm:3.0.0-beta.6.ha.4":
|
||||
version: 3.0.0-beta.6.ha.4
|
||||
resolution: "@home-assistant/webawesome@npm:3.0.0-beta.6.ha.4"
|
||||
"@home-assistant/webawesome@npm:3.0.0-beta.6.ha.5":
|
||||
version: 3.0.0-beta.6.ha.5
|
||||
resolution: "@home-assistant/webawesome@npm:3.0.0-beta.6.ha.5"
|
||||
dependencies:
|
||||
"@ctrl/tinycolor": "npm:4.1.0"
|
||||
"@floating-ui/dom": "npm:^1.6.13"
|
||||
@@ -1955,7 +1955,7 @@ __metadata:
|
||||
lit: "npm:^3.2.1"
|
||||
nanoid: "npm:^5.1.5"
|
||||
qr-creator: "npm:^1.0.0"
|
||||
checksum: 10/d9072b321126ef458468ed2cf040e0b04cb2aff73336c6e742c0cfb25d9fb674b7672e7c9abcf5bcb0aa0b2fe953c20186f0910f485024c827bfe4cf399f10a4
|
||||
checksum: 10/6bfa5e06b91df06402c348bc19ec59a7fe6ed70080989d60a3c6519f99f5dc72da8b42c5dc2cad9d1ab211c51c4c67a74c0e22f21368da3c9f2565cbf8646a90
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9255,7 +9255,7 @@ __metadata:
|
||||
"@codemirror/legacy-modes": "npm:6.5.2"
|
||||
"@codemirror/search": "npm:6.5.11"
|
||||
"@codemirror/state": "npm:6.5.2"
|
||||
"@codemirror/view": "npm:6.38.5"
|
||||
"@codemirror/view": "npm:6.38.6"
|
||||
"@date-fns/tz": "npm:1.4.1"
|
||||
"@egjs/hammerjs": "npm:2.0.17"
|
||||
"@formatjs/intl-datetimeformat": "npm:6.18.2"
|
||||
@@ -9273,7 +9273,7 @@ __metadata:
|
||||
"@fullcalendar/list": "npm:6.1.19"
|
||||
"@fullcalendar/luxon3": "npm:6.1.19"
|
||||
"@fullcalendar/timegrid": "npm:6.1.19"
|
||||
"@home-assistant/webawesome": "npm:3.0.0-beta.6.ha.4"
|
||||
"@home-assistant/webawesome": "npm:3.0.0-beta.6.ha.5"
|
||||
"@lezer/highlight": "npm:1.2.1"
|
||||
"@lit-labs/motion": "npm:1.0.9"
|
||||
"@lit-labs/observers": "npm:2.0.6"
|
||||
|
Reference in New Issue
Block a user