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:
parent
4f8415e8a7
commit
1ce3347c2e
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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)",
|
||||
|
|
|
@ -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''."
|
||||
|
|
Loading…
Reference in New Issue