Add multi select to automations (#20291)

* Add multi select to automations

* allow to clear category, add icons

* use popover

* revert changes to group. by and sort menu, fix dark mode

* ha-menu

* responsive
This commit is contained in:
Bram Kragten 2024-04-02 10:14:17 +02:00 committed by GitHub
parent 4f8415e8a7
commit 1ce3347c2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 492 additions and 68 deletions

View File

@ -22,14 +22,6 @@ export class HaAssistChip extends MdAssistChip {
);
--md-assist-chip-outline-color: var(--outline-color);
--md-assist-chip-label-text-weight: 400;
--ha-assist-chip-filled-container-color: rgba(
var(--rgb-primary-text-color),
0.15
);
--ha-assist-chip-active-container-color: rgba(
var(--rgb-primary-color),
0.15
);
}
/** Material 3 doesn't have a filled chip, so we have to make our own **/
.filled {
@ -52,6 +44,10 @@ export class HaAssistChip extends MdAssistChip {
margin-inline-end: unset;
margin-inline-start: var(--_icon-label-space);
}
::before {
background: var(--ha-assist-chip-container-color);
opacity: var(--ha-assist-chip-container-opacity);
}
:where(.active)::before {
background: var(--ha-assist-chip-active-container-color);
opacity: var(--ha-assist-chip-active-container-opacity);

View File

@ -181,6 +181,13 @@ export class HaDataTable extends LitElement {
this._checkedRowsChanged();
}
public selectAll(): void {
this._checkedRows = this._filteredData
.filter((data) => data.selectable !== false)
.map((data) => data[this.id]);
this._checkedRowsChanged();
}
public connectedCallback() {
super.connectedCallback();
if (this._items.length) {
@ -593,10 +600,7 @@ export class HaDataTable extends LitElement {
private _handleHeaderRowCheckboxClick(ev: Event) {
const checkbox = ev.target as HaCheckbox;
if (checkbox.checked) {
this._checkedRows = this._filteredData
.filter((data) => data.selectable !== false)
.map((data) => data[this.id]);
this._checkedRowsChanged();
this.selectAll();
} else {
this._checkedRows = [];
this._checkedRowsChanged();

View File

@ -0,0 +1,89 @@
import { Button } from "@material/mwc-button";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { FOCUS_TARGET } from "../dialogs/make-dialog-manager";
import type { HaIconButton } from "./ha-icon-button";
import "./ha-menu";
import type { HaMenu } from "./ha-menu";
@customElement("ha-button-menu-new")
export class HaButtonMenuNew extends LitElement {
protected readonly [FOCUS_TARGET];
@property({ type: Boolean }) public disabled = false;
@property() public positioning?: "fixed" | "absolute" | "popover";
@property({ type: Boolean, attribute: "has-overflow" }) public hasOverflow =
false;
@query("ha-menu", true) private _menu!: HaMenu;
public get items() {
return this._menu.items;
}
public override focus() {
if (this._menu.open) {
this._menu.focus();
} else {
this._triggerButton?.focus();
}
}
protected render(): TemplateResult {
return html`
<div @click=${this._handleClick}>
<slot name="trigger" @slotchange=${this._setTriggerAria}></slot>
</div>
<ha-menu
.positioning=${this.positioning}
.hasOverflow=${this.hasOverflow}
>
<slot></slot>
</ha-menu>
`;
}
private _handleClick(): void {
if (this.disabled) {
return;
}
this._menu.anchorElement = this;
if (this._menu.open) {
this._menu.close();
} else {
this._menu.show();
}
}
private get _triggerButton() {
return this.querySelector(
'ha-icon-button[slot="trigger"], mwc-button[slot="trigger"], ha-assist-chip[slot="trigger"]'
) as HaIconButton | Button | null;
}
private _setTriggerAria() {
if (this._triggerButton) {
this._triggerButton.ariaHasPopup = "menu";
}
}
static get styles(): CSSResultGroup {
return css`
:host {
display: inline-block;
position: relative;
}
::slotted([disabled]) {
color: var(--disabled-text-color);
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-button-menu-new": HaButtonMenuNew;
}
}

View File

@ -0,0 +1,38 @@
import { customElement } from "lit/decorators";
import "element-internals-polyfill";
import { CSSResult, css } from "lit";
import { MdSubMenu } from "@material/web/menu/sub-menu";
@customElement("ha-sub-menu")
// @ts-expect-error
export class HaSubMenu extends MdSubMenu {
static override styles: CSSResult[] = [
MdSubMenu.styles,
css`
:host {
--ha-icon-display: block;
--md-sys-color-primary: var(--primary-text-color);
--md-sys-color-on-primary: var(--primary-text-color);
--md-sys-color-secondary: var(--secondary-text-color);
--md-sys-color-surface: var(--card-background-color);
--md-sys-color-on-surface: var(--primary-text-color);
--md-sys-color-on-surface-variant: var(--secondary-text-color);
--md-sys-color-secondary-container: rgba(
var(--rgb-primary-color),
0.15
);
--md-sys-color-on-secondary-container: var(--text-primary-color);
--mdc-icon-size: 16px;
--md-sys-color-on-primary-container: var(--primary-text-color);
--md-sys-color-on-secondary-container: var(--primary-text-color);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-sub-menu": HaSubMenu;
}
}

View File

@ -1,12 +1,13 @@
import { ResizeController } from "@lit-labs/observers/resize-controller";
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
import "@material/mwc-button/mwc-button";
import "@material/web/divider/divider";
import {
mdiArrowDown,
mdiArrowUp,
mdiClose,
mdiFilterVariantRemove,
mdiFilterVariant,
mdiFilterVariantRemove,
mdiFormatListChecks,
mdiMenuDown,
} from "@mdi/js";
@ -31,14 +32,14 @@ import type {
HaDataTable,
SortingDirection,
} from "../components/data-table/ha-data-table";
import "../components/ha-button-menu-new";
import "../components/ha-dialog";
import "../components/search-input-outlined";
import "../components/ha-menu";
import { HaMenu } from "../components/ha-menu";
import "../components/ha-menu-item";
import "../components/search-input-outlined";
import type { HomeAssistant, Route } from "../types";
import "./hass-tabs-subpage";
import type { PageNavigation } from "./hass-tabs-subpage";
import type { HaMenu } from "../components/ha-menu";
declare global {
// for fire event
@ -227,7 +228,7 @@ export class HaTabsSubpageDataTable extends LitElement {
class="has-dropdown select-mode-chip"
.active=${this._selectMode}
@click=${this._enableSelectMode}
.label=${localize(
.title=${localize(
"ui.components.subpage-data-table.enter_selection_mode"
)}
>
@ -255,8 +256,11 @@ export class HaTabsSubpageDataTable extends LitElement {
id="sort-by-anchor"
@click=${this._toggleSortBy}
>
<ha-svg-icon slot="trailing-icon" .path=${mdiMenuDown}></ha-svg-icon
></ha-assist-chip>
<ha-svg-icon
slot="trailing-icon"
.path=${mdiMenuDown}
></ha-svg-icon>
</ha-assist-chip>
`
: nothing;
@ -293,7 +297,7 @@ export class HaTabsSubpageDataTable extends LitElement {
>
${this._selectMode
? html`<div class="selection-bar" slot="toolbar">
<div class="center-vertical">
<div class="selection-controls">
<ha-icon-button
.path=${mdiClose}
@click=${this._disableSelectMode}
@ -301,6 +305,37 @@ export class HaTabsSubpageDataTable extends LitElement {
"ui.components.subpage-data-table.exit_selection_mode"
)}
></ha-icon-button>
<ha-button-menu-new positioning="absolute">
<ha-assist-chip
.label=${localize(
"ui.components.subpage-data-table.select"
)}
slot="trigger"
>
<ha-svg-icon
slot="icon"
.path=${mdiFormatListChecks}
></ha-svg-icon>
<ha-svg-icon
slot="trailing-icon"
.path=${mdiMenuDown}
></ha-svg-icon
></ha-assist-chip>
<ha-menu-item .value=${undefined} @click=${this._selectAll}
>${localize("ui.components.subpage-data-table.select_all")}
</ha-menu-item>
<ha-menu-item .value=${undefined} @click=${this._selectNone}
>${localize("ui.components.subpage-data-table.select_none")}
</ha-menu-item>
<md-divider role="separator" tabindex="-1"></md-divider>
<ha-menu-item
.value=${undefined}
@click=${this._disableSelectMode}
>${localize(
"ui.components.subpage-data-table.close_select_mode"
)}
</ha-menu-item>
</ha-button-menu-new>
<p>
${localize("ui.components.subpage-data-table.selected", {
selected: this.selected || "0",
@ -440,16 +475,15 @@ export class HaTabsSubpageDataTable extends LitElement {
`
: nothing
)}
<li divider role="separator"></li>
<md-divider role="separator" tabindex="-1"></md-divider>
<ha-menu-item
.value=${undefined}
@click=${this._handleGroupBy}
.selected=${this._groupColumn === undefined}
class=${classMap({ selected: this._groupColumn === undefined })}
>${localize(
"ui.components.subpage-data-table.dont_group_by"
)}</ha-menu-item
>
${localize("ui.components.subpage-data-table.dont_group_by")}
</ha-menu-item>
</ha-menu>
<ha-menu anchor="sort-by-anchor" id="sort-by-menu" positioning="fixed">
${Object.entries(this.columns).map(([id, column]) =>
@ -458,6 +492,7 @@ export class HaTabsSubpageDataTable extends LitElement {
<ha-menu-item
.value=${id}
@click=${this._handleSortBy}
keep-open
.selected=${id === this._sortColumn}
class=${classMap({ selected: id === this._sortColumn })}
>
@ -494,8 +529,6 @@ export class HaTabsSubpageDataTable extends LitElement {
}
private _handleSortBy(ev) {
ev.stopPropagation();
ev.preventDefault();
const columnId = ev.currentTarget.value;
if (!this._sortDirection || this._sortColumn !== columnId) {
this._sortDirection = "asc";
@ -520,6 +553,14 @@ export class HaTabsSubpageDataTable extends LitElement {
this._dataTable.clearSelection();
}
private _selectAll() {
this._dataTable.selectAll();
}
private _selectNone() {
this._dataTable.clearSelection();
}
private _handleSearchChange(ev: CustomEvent) {
if (this.filter === ev.detail.value) {
return;
@ -687,23 +728,31 @@ export class HaTabsSubpageDataTable extends LitElement {
padding: 8px 12px;
box-sizing: border-box;
font-size: 14px;
--ha-assist-chip-container-color: var(--primary-background-color);
}
.selection-controls {
display: flex;
align-items: center;
gap: 8px;
}
.selection-controls p {
margin-left: 8px;
margin-inline-start: 8px;
margin-inline-end: initial;
}
.center-vertical {
display: flex;
align-items: center;
gap: 8px;
}
.relative {
position: relative;
}
.selection-bar p {
margin-left: 16px;
margin-inline-start: 16px;
margin-inline-end: initial;
}
ha-assist-chip {
--ha-assist-chip-container-shape: 10px;
}
@ -732,8 +781,10 @@ export class HaTabsSubpageDataTable extends LitElement {
display: flex;
flex-direction: column;
}
#sort-by-anchor,
#group-by-anchor {
#group-by-anchor,
ha-button-menu-new ha-assist-chip {
--md-assist-chip-trailing-space: 8px;
}
`;

View File

@ -1,17 +1,22 @@
import { consume } from "@lit-labs/context";
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
import {
mdiChevronRight,
mdiCog,
mdiContentDuplicate,
mdiDelete,
mdiDotsVertical,
mdiHelpCircle,
mdiInformationOutline,
mdiMenuDown,
mdiPlay,
mdiPlayCircleOutline,
mdiPlus,
mdiRobotHappy,
mdiStopCircleOutline,
mdiTag,
mdiToggleSwitch,
mdiToggleSwitchOffOutline,
mdiTransitConnection,
} from "@mdi/js";
import { differenceInDays } from "date-fns/esm";
@ -28,6 +33,7 @@ import {
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { computeCssColor } from "../../../common/color/compute-color";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
import { relativeTime } from "../../../common/datetime/relative_time";
@ -39,6 +45,7 @@ import "../../../components/chips/ha-assist-chip";
import type {
DataTableColumnContainer,
RowClickedEvent,
SelectionChangedEvent,
} from "../../../components/data-table/ha-data-table";
import "../../../components/data-table/ha-data-table-labels";
import "../../../components/entity/ha-entity-toggle";
@ -51,6 +58,8 @@ import "../../../components/ha-filter-floor-areas";
import "../../../components/ha-filter-labels";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-overflow-menu";
import "../../../components/ha-menu-item";
import "../../../components/ha-sub-menu";
import "../../../components/ha-svg-icon";
import {
AutomationEntity,
@ -67,7 +76,11 @@ import {
} from "../../../data/category_registry";
import { fullEntitiesContext } from "../../../data/context";
import { UNAVAILABLE } from "../../../data/entity";
import { EntityRegistryEntry } from "../../../data/entity_registry";
import {
EntityRegistryEntry,
UpdateEntityRegistryEntryResult,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
import {
LabelRegistryEntry,
subscribeLabelRegistry,
@ -80,8 +93,9 @@ import {
import "../../../layouts/hass-tabs-subpage-data-table";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import { HomeAssistant, Route, ServiceCallResponse } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { turnOnOffEntity } from "../../lovelace/common/entity/turn-on-off-entity";
import { showAssignCategoryDialog } from "../category/show-dialog-assign-category";
import { configSections } from "../ha-panel-config";
import { showNewAutomationDialog } from "./show-dialog-new-automation";
@ -117,6 +131,8 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
@state() private _expandedFilter?: string;
@state() private _selected: string[] = [];
@state()
_categories!: CategoryRegistryEntry[];
@ -374,6 +390,40 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
}
protected render(): TemplateResult {
const categoryItems = html`${this._categories?.map(
(category) =>
html`<ha-menu-item
.value=${category.category_id}
@click=${this._handleBulkCategory}
>
${category.icon
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
<div slot="headline">${category.name}</div>
</ha-menu-item>`
)}
<ha-menu-item .value=${null} @click=${this._handleBulkCategory}>
<div slot="headline">
${this.hass.localize(
"ui.panel.config.automation.picker.bulk_actions.no_category"
)}
</div>
</ha-menu-item>`;
const labelItems = html` ${this._labels?.map((label) => {
const color = label.color ? computeCssColor(label.color) : undefined;
return html`<ha-menu-item
.value=${label.label_id}
@click=${this._handleBulkLabel}
>
<ha-label style=${color ? `--color: ${color}` : ""}>
${label.icon
? html`<ha-icon slot="icon" .icon=${label.icon}></ha-icon>`
: nothing}
${label.name}
</ha-label>
</ha-menu-item>`;
})}`;
return html`
<hass-tabs-subpage-data-table
.hass=${this.hass}
@ -382,10 +432,14 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
id="entity_id"
.route=${this.route}
.tabs=${configSections.automations}
selectable
.selected=${this._selected.length}
@selection-changed=${this._handleSelectionChanged}
hasFilters
.filters=${Object.values(this._filters).filter(
(filter) => filter.value?.length
).length}
.filters=${
Object.values(this._filters).filter((filter) => filter.value?.length)
.length
}
.columns=${this._columns(
this.narrow,
this.hass.localize,
@ -474,36 +528,156 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
.narrow=${this.narrow}
@expanded-changed=${this._filterExpanded}
></ha-filter-blueprints>
${!this.automations.length
? html`<div class="empty" slot="empty">
<ha-svg-icon .path=${mdiRobotHappy}></ha-svg-icon>
<h1>
${
!this.narrow
? html`<ha-button-menu-new slot="selection-bar">
<ha-assist-chip
slot="trigger"
.label=${this.hass.localize(
"ui.panel.config.automation.picker.bulk_actions.move_category"
)}
>
<ha-svg-icon
slot="trailing-icon"
.path=${mdiMenuDown}
></ha-svg-icon>
</ha-assist-chip>
${categoryItems}
</ha-button-menu-new>
${this.hass.dockedSidebar === "docked"
? nothing
: html`<ha-button-menu-new slot="selection-bar">
<ha-assist-chip
slot="trigger"
.label=${this.hass.localize(
"ui.panel.config.automation.picker.bulk_actions.add_label"
)}
>
<ha-svg-icon
slot="trailing-icon"
.path=${mdiMenuDown}
></ha-svg-icon>
</ha-assist-chip>
${labelItems}
</ha-button-menu-new>`}`
: nothing
}
<ha-button-menu-new has-overflow slot="selection-bar">
${
this.narrow
? html`<ha-assist-chip
.label=${this.hass.localize(
"ui.panel.config.automation.picker.bulk_action"
)}
slot="trigger"
>
<ha-svg-icon
slot="trailing-icon"
.path=${mdiMenuDown}
></ha-svg-icon>
</ha-assist-chip>`
: html`<ha-icon-button
.path=${mdiDotsVertical}
.label=${"ui.panel.config.automation.picker.bulk_action"}
slot="trigger"
></ha-icon-button>`
}
<ha-svg-icon
slot="trailing-icon"
.path=${mdiMenuDown}
></ha-svg-icon
></ha-assist-chip>
${
this.narrow
? html`<ha-sub-menu>
<ha-menu-item slot="item">
<div slot="headline">
${this.hass.localize(
"ui.panel.config.automation.picker.bulk_actions.move_category"
)}
</div>
<ha-svg-icon
slot="end"
.path=${mdiChevronRight}
></ha-svg-icon>
</ha-menu-item>
<ha-menu slot="menu">${categoryItems}</ha-menu>
</ha-sub-menu>`
: nothing
}
${
this.narrow || this.hass.dockedSidebar === "docked"
? html` <ha-sub-menu>
<ha-menu-item slot="item">
<div slot="headline">
${this.hass.localize(
"ui.panel.config.automation.picker.bulk_actions.add_label"
)}
</div>
<ha-svg-icon
slot="end"
.path=${mdiChevronRight}
></ha-svg-icon>
</ha-menu-item>
<ha-menu slot="menu">${labelItems}</ha-menu>
</ha-sub-menu>`
: nothing
}
<ha-menu-item @click=${this._handleBulkEnable}>
<ha-svg-icon slot="start" .path=${mdiToggleSwitch}></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
"ui.panel.config.automation.picker.empty_header"
"ui.panel.config.automation.picker.bulk_actions.enable"
)}
</h1>
<p>
</div>
</ha-menu-item>
<ha-menu-item @click=${this._handleBulkDisable}>
<ha-svg-icon
slot="start"
.path=${mdiToggleSwitchOffOutline}
></ha-svg-icon>
<div slot="headline">
${this.hass.localize(
"ui.panel.config.automation.picker.empty_text_1"
"ui.panel.config.automation.picker.bulk_actions.disable"
)}
</p>
<p>
${this.hass.localize(
"ui.panel.config.automation.picker.empty_text_2",
{ user: this.hass.user?.name || "Alice" }
)}
</p>
<a
href=${documentationUrl(this.hass, "/docs/automation/editor/")}
target="_blank"
rel="noreferrer"
>
<ha-button>
${this.hass.localize("ui.panel.config.common.learn_more")}
</ha-button>
</a>
</div>`
: nothing}
</div>
</ha-menu-item>
</ha-button-menu-new>
${
!this.automations.length
? html`<div class="empty" slot="empty">
<ha-svg-icon .path=${mdiRobotHappy}></ha-svg-icon>
<h1>
${this.hass.localize(
"ui.panel.config.automation.picker.empty_header"
)}
</h1>
<p>
${this.hass.localize(
"ui.panel.config.automation.picker.empty_text_1"
)}
</p>
<p>
${this.hass.localize(
"ui.panel.config.automation.picker.empty_text_2",
{ user: this.hass.user?.name || "Alice" }
)}
</p>
<a
href=${documentationUrl(
this.hass,
"/docs/automation/editor/"
)}
target="_blank"
rel="noreferrer"
>
<ha-button>
${this.hass.localize("ui.panel.config.common.learn_more")}
</ha-button>
</a>
</div>`
: nothing
}
<ha-fab
slot="fab"
.label=${this.hass.localize(
@ -791,6 +965,12 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
}
}
private _handleSelectionChanged(
ev: HASSDomEvent<SelectionChangedEvent>
): void {
this._selected = ev.detail.value;
}
private _createNew() {
if (isComponentLoaded(this.hass, "blueprint")) {
showNewAutomationDialog(this, { mode: "automation" });
@ -799,6 +979,48 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
}
}
private async _handleBulkCategory(ev) {
const category = ev.currentTarget.value;
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
this._selected.forEach((entityId) => {
promises.push(
updateEntityRegistryEntry(this.hass, entityId, {
categories: { automation: category },
})
);
});
await Promise.all(promises);
}
private async _handleBulkLabel(ev) {
const label = ev.currentTarget.value;
const promises: Promise<UpdateEntityRegistryEntryResult>[] = [];
this._selected.forEach((entityId) => {
promises.push(
updateEntityRegistryEntry(this.hass, entityId, {
labels: this.hass.entities[entityId].labels.concat(label),
})
);
});
await Promise.all(promises);
}
private async _handleBulkEnable() {
const promises: Promise<ServiceCallResponse>[] = [];
this._selected.forEach((entityId) => {
promises.push(turnOnOffEntity(this.hass, entityId, true));
});
await Promise.all(promises);
}
private async _handleBulkDisable() {
const promises: Promise<ServiceCallResponse>[] = [];
this._selected.forEach((entityId) => {
promises.push(turnOnOffEntity(this.hass, entityId, false));
});
await Promise.all(promises);
}
static get styles(): CSSResultGroup {
return [
haStyle,
@ -814,6 +1036,16 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
--mdc-icon-size: 80px;
max-width: 500px;
}
ha-assist-chip {
--ha-assist-chip-container-shape: 10px;
}
ha-button-menu-new ha-assist-chip {
--md-assist-chip-trailing-space: 8px;
}
ha-label {
--ha-label-background-color: var(--color, var(--grey-color));
--ha-label-background-opacity: 0.5;
}
`,
];
}

View File

@ -527,11 +527,11 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
.filters=${Object.values(this._filters).filter(
(filter) => filter.value?.length
).length}
.selected=${this._selectedEntities.length}
.filter=${this._filter}
selectable
clickable
.selected=${this._selectedEntities.length}
@selection-changed=${this._handleSelectionChanged}
clickable
@clear-filter=${this._clearFilter}
@search-changed=${this._handleSearchChange}
@row-click=${this._openEditEntry}

View File

@ -143,7 +143,10 @@ export const derivedStyles = {
"mdc-select-disabled-ink-color": "var(--input-disabled-ink-color)",
"mdc-select-dropdown-icon-color": "var(--input-dropdown-icon-color)",
"mdc-select-disabled-dropdown-icon-color": "var(--input-disabled-ink-color)",
"ha-assist-chip-filled-container-color":
"rgba(var(--rgb-primary-text-color),0.15)",
"ha-assist-chip-active-container-color":
"rgba(var(--rgb-primary-color),0.15)",
"chip-background-color": "rgba(var(--rgb-primary-text-color), 0.15)",
// Vaadin
"material-body-text-color": "var(--primary-text-color)",

View File

@ -509,7 +509,10 @@
"group_by": "Group by {groupColumn}",
"dont_group_by": "Don't group",
"select": "Select",
"selected": "Selected {selected}"
"selected": "Selected {selected}",
"close_select_mode": "Close selection mode",
"select_all": "Select all",
"select_none": "Select none"
},
"config-entry-picker": {
"config_entry": "Integration"
@ -2694,6 +2697,14 @@
"state": "State",
"category": "Category"
},
"bulk_action": "Action",
"bulk_actions": {
"move_category": "Move to category",
"no_category": "No category",
"add_label": "Add label",
"enable": "Enable",
"disable": "Disable"
},
"empty_header": "Start automating",
"empty_text_1": "Automations make Home Assistant automatically respond to things happening in and around your home.",
"empty_text_2": "Automations connect triggers to actions in a ''when trigger then action'' fashion with optional conditions. For example: ''When the sun sets and if {user} is home, then turn on the lights''."