diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index ea1e0c4924..06d515a115 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -533,15 +533,20 @@ export class HaDataTable extends LitElement { }, {}); const groupedItems: DataTableRowData[] = []; Object.entries(sorted).forEach(([groupName, rows]) => { - groupedItems.push({ - append: true, - content: html`
- ${groupName === UNDEFINED_GROUP_KEY ? "" : groupName || ""} -
`, - }); + if ( + groupName !== UNDEFINED_GROUP_KEY || + Object.keys(sorted).length > 1 + ) { + groupedItems.push({ + append: true, + content: html`
+ ${groupName === UNDEFINED_GROUP_KEY ? "" : groupName || ""} +
`, + }); + } groupedItems.push(...rows); }); diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index 76205ba46a..2f02ae82a9 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -1,3 +1,5 @@ +import { consume } from "@lit-labs/context"; +import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; import { mdiContentDuplicate, mdiDelete, @@ -7,38 +9,59 @@ import { mdiPencilOff, mdiPlay, mdiPlus, + mdiTag, } from "@mdi/js"; -import "@lrnwebcomponents/simple-tooltip/simple-tooltip"; +import { differenceInDays } from "date-fns/esm"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { - css, CSSResultGroup, - html, LitElement, - nothing, TemplateResult, + css, + html, + nothing, } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; -import { differenceInDays } from "date-fns/esm"; -import { fireEvent, HASSDomEvent } from "../../../common/dom/fire_event"; +import { formatShortDateTime } from "../../../common/datetime/format_date_time"; +import { relativeTime } from "../../../common/datetime/relative_time"; +import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { navigate } from "../../../common/navigate"; +import { LocalizeFunc } from "../../../common/translations/localize"; import { DataTableColumnContainer, RowClickedEvent, } from "../../../components/data-table/ha-data-table"; -import "../../../components/ha-fab"; +import "../../../components/data-table/ha-data-table-labels"; import "../../../components/ha-button"; +import "../../../components/ha-fab"; +import "../../../components/ha-filter-categories"; +import "../../../components/ha-filter-devices"; +import "../../../components/ha-filter-entities"; +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-state-icon"; import "../../../components/ha-svg-icon"; -import "../../../components/ha-icon-overflow-menu"; +import { + CategoryRegistryEntry, + subscribeCategoryRegistry, +} from "../../../data/category_registry"; +import { fullEntitiesContext } from "../../../data/context"; +import { isUnavailableState } from "../../../data/entity"; +import { EntityRegistryEntry } from "../../../data/entity_registry"; import { forwardHaptic } from "../../../data/haptics"; import { + LabelRegistryEntry, + subscribeLabelRegistry, +} from "../../../data/label_registry"; +import { + SceneEntity, activateScene, deleteScene, getSceneConfig, - SceneEntity, showSceneEditor, } from "../../../data/scene"; import { @@ -46,21 +69,22 @@ import { showConfirmationDialog, } from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-tabs-subpage-data-table"; +import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { showToast } from "../../../util/toast"; +import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import { configSections } from "../ha-panel-config"; -import { formatShortDateTime } from "../../../common/datetime/format_date_time"; -import { relativeTime } from "../../../common/datetime/relative_time"; -import { isUnavailableState } from "../../../data/entity"; type SceneItem = SceneEntity & { name: string; + category: string | undefined; + labels: LabelRegistryEntry[]; }; @customElement("ha-scene-dashboard") -class HaSceneDashboard extends LitElement { +class HaSceneDashboard extends SubscribeMixin(LitElement) { @property({ attribute: false }) public hass!: HomeAssistant; @property({ type: Boolean }) public narrow = false; @@ -75,8 +99,31 @@ class HaSceneDashboard extends LitElement { @state() private _filteredScenes?: string[] | null; + @state() private _filters: Record< + string, + { value: string[] | undefined; items: Set | undefined } + > = {}; + + @state() private _expandedFilter?: string; + + @state() + _categories!: CategoryRegistryEntry[]; + + @state() + _labels!: LabelRegistryEntry[]; + + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; + private _scenes = memoizeOne( - (scenes: SceneEntity[], filteredScenes?: string[] | null): SceneItem[] => { + ( + scenes: SceneEntity[], + entityReg: EntityRegistryEntry[], + categoryReg?: CategoryRegistryEntry[], + labelReg?: LabelRegistryEntry[], + filteredScenes?: string[] | null + ): SceneItem[] => { if (filteredScenes === null) { return []; } @@ -84,21 +131,32 @@ class HaSceneDashboard extends LitElement { filteredScenes ? scenes.filter((scene) => filteredScenes!.includes(scene.entity_id)) : scenes - ).map((scene) => ({ - ...scene, - name: computeStateName(scene), - })); + ).map((scene) => { + const entityRegEntry = entityReg.find( + (reg) => reg.entity_id === scene.entity_id + ); + const category = entityRegEntry?.categories.scene; + const labels = labelReg && entityRegEntry?.labels; + return { + ...scene, + name: computeStateName(scene), + category: category + ? categoryReg?.find((cat) => cat.category_id === category)?.name + : undefined, + labels: (labels || []).map( + (lbl) => labelReg!.find((label) => label.label_id === lbl)! + ), + }; + }); } ); private _columns = memoizeOne( - (_language, narrow): DataTableColumnContainer => { + (narrow, localize: LocalizeFunc): DataTableColumnContainer => { const columns: DataTableColumnContainer = { icon: { title: "", - label: this.hass.localize( - "ui.panel.config.scene.picker.headers.state" - ), + label: localize("ui.panel.config.scene.picker.headers.state"), type: "icon", template: (scene) => html` html` +
${scene.name}
+ ${scene.labels.length + ? html`` + : nothing} + `, + }, + category: { + title: localize("ui.panel.config.scene.picker.headers.category"), + hidden: true, + groupable: true, + filterable: true, + sortable: true, + }, + labels: { + title: "", + hidden: true, + filterable: true, + template: (scene) => scene.labels.map((lbl) => lbl.name).join(" "), }, }; if (!narrow) { columns.state = { - title: this.hass.localize( + title: localize( "ui.panel.config.scene.picker.headers.last_activated" ), sortable: true, @@ -128,7 +206,7 @@ class HaSceneDashboard extends LitElement { template: (scene) => { const lastActivated = scene.state; if (!lastActivated || isUnavailableState(lastActivated)) { - return this.hass.localize("ui.components.relative_time.never"); + return localize("ui.components.relative_time.never"); } const date = new Date(scene.state); const now = new Date(); @@ -161,7 +239,7 @@ class HaSceneDashboard extends LitElement { }; columns.actions = { title: "", - width: "72px", + width: "64px", type: "overflow-menu", template: (scene) => html` this._activateScene(scene), }, + { + path: mdiTag, + label: this.hass.localize( + `ui.panel.config.scene.picker.${scene.category ? "edit_category" : "assign_category"}` + ), + action: () => this._editCategory(scene), + }, { divider: true, }, @@ -212,6 +297,17 @@ class HaSceneDashboard extends LitElement { } ); + protected hassSubscribe(): (UnsubscribeFunc | Promise)[] { + return [ + subscribeCategoryRegistry(this.hass.connection, "scene", (categories) => { + this._categories = categories; + }), + subscribeLabelRegistry(this.hass.connection, (labels) => { + this._labels = labels; + }), + ]; + } + protected render(): TemplateResult { return html` filter.value?.length + ).length} + .columns=${this._columns(this.narrow, this.hass.localize)} id="entity_id" - .data=${this._scenes(this.scenes, this._filteredScenes)} + initialGroupColumn="category" + .data=${this._scenes( + this.scenes, + this._entityReg, + this._categories, + this._labels, + this._filteredScenes + )} .empty=${!this.scenes.length} .activeFilters=${this._activeFilters} .noDataText=${this.hass.localize( @@ -239,6 +346,57 @@ class HaSceneDashboard extends LitElement { .label=${this.hass.localize("ui.common.help")} .path=${mdiHelpCircle} > + + + + + + + ${!this.scenes.length ? html`
@@ -275,6 +433,95 @@ class HaSceneDashboard extends LitElement { `; } + private _filterExpanded(ev) { + if (ev.detail.expanded) { + this._expandedFilter = ev.target.localName; + } else if (this._expandedFilter === ev.target.localName) { + this._expandedFilter = undefined; + } + } + + private _labelClicked = (ev: CustomEvent) => { + const label = ev.detail.label; + this._filters = { + ...this._filters, + "ha-filter-labels": { + value: [label.label_id], + items: undefined, + }, + }; + this._applyFilters(); + }; + + private _filterChanged(ev) { + const type = ev.target.localName; + this._filters[type] = ev.detail; + this._applyFilters(); + } + + private _applyFilters() { + const filters = Object.entries(this._filters); + let items: Set | undefined; + for (const [key, filter] of filters) { + if (filter.items) { + if (!items) { + items = filter.items; + continue; + } + items = + "intersection" in items + ? // @ts-ignore + items.intersection(filter.items) + : new Set([...items].filter((x) => filter.items!.has(x))); + } + if (key === "ha-filter-categories" && filter.value?.length) { + const categoryItems: Set = new Set(); + this.scenes + .filter( + (scene) => + filter.value![0] === + this._entityReg.find((reg) => reg.entity_id === scene.entity_id) + ?.categories.scene + ) + .forEach((scene) => categoryItems.add(scene.entity_id)); + if (!items) { + items = categoryItems; + continue; + } + items = + "intersection" in items + ? // @ts-ignore + items.intersection(categoryItems) + : new Set([...items].filter((x) => categoryItems!.has(x))); + } + if (key === "ha-filter-labels" && filter.value?.length) { + const labelItems: Set = new Set(); + this.scenes + .filter((scene) => + this._entityReg + .find((reg) => reg.entity_id === scene.entity_id) + ?.labels.some((lbl) => filter.value!.includes(lbl)) + ) + .forEach((scene) => labelItems.add(scene.entity_id)); + if (!items) { + items = labelItems; + continue; + } + items = + "intersection" in items + ? // @ts-ignore + items.intersection(labelItems) + : new Set([...items].filter((x) => labelItems!.has(x))); + } + } + this._filteredScenes = items ? [...items] : undefined; + } + + private _clearFilter() { + this._filters = {}; + this._applyFilters(); + } + private _handleRowClicked(ev: HASSDomEvent) { const scene = this.scenes.find((a) => a.entity_id === ev.detail.id); @@ -283,9 +530,25 @@ class HaSceneDashboard extends LitElement { } } - private _clearFilter() { - this._filteredScenes = undefined; - this._activeFilters = undefined; + private _editCategory(scene: any) { + const entityReg = this._entityReg.find( + (reg) => reg.entity_id === scene.entity_id + ); + if (!entityReg) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.scene.picker.no_category_support" + ), + text: this.hass.localize( + "ui.panel.config.scene.picker.no_category_entity_reg" + ), + }); + return; + } + showAssignCategoryDialog(this, { + scope: "scene", + entityReg, + }); } private _showInfo(scene: SceneEntity) { @@ -359,6 +622,9 @@ class HaSceneDashboard extends LitElement { return [ haStyle, css` + hass-tabs-subpage-data-table { + --data-table-row-height: 60px; + } a { text-decoration: none; } diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index 0e4c1d1ec7..4449f1e7fe 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -1,3 +1,4 @@ +import { consume } from "@lit-labs/context"; import { mdiContentDuplicate, mdiDelete, @@ -6,9 +7,11 @@ import { mdiPlay, mdiPlus, mdiScriptText, + mdiTag, mdiTransitConnection, } from "@mdi/js"; import { differenceInDays } from "date-fns/esm"; +import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { CSSResultGroup, LitElement, @@ -26,17 +29,33 @@ import { relativeTime } from "../../../common/datetime/relative_time"; import { HASSDomEvent, fireEvent } from "../../../common/dom/fire_event"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { navigate } from "../../../common/navigate"; +import { LocalizeFunc } from "../../../common/translations/localize"; import { DataTableColumnContainer, RowClickedEvent, } from "../../../components/data-table/ha-data-table"; +import "../../../components/data-table/ha-data-table-labels"; import "../../../components/ha-fab"; +import "../../../components/ha-filter-blueprints"; +import "../../../components/ha-filter-categories"; +import "../../../components/ha-filter-devices"; +import "../../../components/ha-filter-entities"; +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-svg-icon"; -import { fetchBlueprints } from "../../../data/blueprint"; +import { + CategoryRegistryEntry, + subscribeCategoryRegistry, +} from "../../../data/category_registry"; +import { fullEntitiesContext } from "../../../data/context"; import { UNAVAILABLE } from "../../../data/entity"; import { EntityRegistryEntry } from "../../../data/entity_registry"; +import { + LabelRegistryEntry, + subscribeLabelRegistry, +} from "../../../data/label_registry"; import { ScriptEntity, deleteScript, @@ -51,19 +70,23 @@ import { showConfirmationDialog, } from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-tabs-subpage-data-table"; +import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import { showToast } from "../../../util/toast"; import { showNewAutomationDialog } from "../automation/show-dialog-new-automation"; +import { showAssignCategoryDialog } from "../category/show-dialog-assign-category"; import { configSections } from "../ha-panel-config"; type ScriptItem = ScriptEntity & { name: string; + category: string | undefined; + labels: LabelRegistryEntry[]; }; @customElement("ha-script-picker") -class HaScriptPicker extends LitElement { +class HaScriptPicker extends SubscribeMixin(LitElement) { @property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public scripts!: ScriptEntity[]; @@ -82,9 +105,29 @@ class HaScriptPicker extends LitElement { @state() private _filteredScripts?: string[] | null; + @state() private _filters: Record< + string, + { value: string[] | undefined; items: Set | undefined } + > = {}; + + @state() private _expandedFilter?: string; + + @state() + _categories!: CategoryRegistryEntry[]; + + @state() + _labels!: LabelRegistryEntry[]; + + @state() + @consume({ context: fullEntitiesContext, subscribe: true }) + _entityReg!: EntityRegistryEntry[]; + private _scripts = memoizeOne( ( scripts: ScriptEntity[], + entityReg: EntityRegistryEntry[], + categoryReg?: CategoryRegistryEntry[], + labelReg?: LabelRegistryEntry[], filteredScripts?: string[] | null ): ScriptItem[] => { if (filteredScripts === null) { @@ -96,22 +139,37 @@ class HaScriptPicker extends LitElement { filteredScripts!.includes(script.entity_id) ) : scripts - ).map((script) => ({ - ...script, - name: computeStateName(script), - last_triggered: script.attributes.last_triggered || undefined, - })); + ).map((script) => { + const entityRegEntry = entityReg.find( + (reg) => reg.entity_id === script.entity_id + ); + const category = entityRegEntry?.categories.script; + const labels = labelReg && entityRegEntry?.labels; + return { + ...script, + name: computeStateName(script), + last_triggered: script.attributes.last_triggered || undefined, + category: category + ? categoryReg?.find((cat) => cat.category_id === category)?.name + : undefined, + labels: (labels || []).map( + (lbl) => labelReg!.find((label) => label.label_id === lbl)! + ), + }; + }); } ); private _columns = memoizeOne( - (narrow, _locale): DataTableColumnContainer => { + ( + narrow, + localize: LocalizeFunc, + locale: HomeAssistant["locale"] + ): DataTableColumnContainer => { const columns: DataTableColumnContainer = { icon: { title: "", - label: this.hass.localize( - "ui.panel.config.script.picker.headers.state" - ), + label: localize("ui.panel.config.script.picker.headers.state"), type: "icon", template: (script) => html``, }, name: { - title: this.hass.localize( - "ui.panel.config.script.picker.headers.name" - ), + title: localize("ui.panel.config.script.picker.headers.name"), main: true, sortable: true, filterable: true, direction: "asc", grows: true, - template: narrow - ? (script) => { - const date = new Date(script.last_triggered); - const now = new Date(); - const dayDifference = differenceInDays(now, date); - return html` - ${script.name} -
+ template: (script) => { + const date = new Date(script.last_triggered); + const now = new Date(); + const dayDifference = differenceInDays(now, date); + return html` +
${script.name}
+ ${narrow + ? html`
${this.hass.localize("ui.card.automation.last_triggered")}: - ${script.last_triggered + ${script.attributes.last_triggered ? dayDifference > 3 - ? formatShortDateTime( - date, - this.hass.locale, - this.hass.config - ) - : relativeTime(date, this.hass.locale) - : this.hass.localize("ui.components.relative_time.never")} -
- `; - } - : undefined, + ? formatShortDateTime(date, locale, this.hass.config) + : relativeTime(date, locale) + : localize("ui.components.relative_time.never")} +
` + : nothing} + ${script.labels.length + ? html`` + : nothing} + `; + }, + }, + category: { + title: localize("ui.panel.config.script.picker.headers.category"), + hidden: true, + groupable: true, + filterable: true, + sortable: true, + }, + labels: { + title: "", + hidden: true, + filterable: true, + template: (script) => script.labels.map((lbl) => lbl.name).join(" "), }, }; if (!narrow) { columns.last_triggered = { sortable: true, width: "40%", - title: this.hass.localize("ui.card.automation.last_triggered"), + title: localize("ui.card.automation.last_triggered"), template: (script) => { const date = new Date(script.last_triggered); const now = new Date(); @@ -182,7 +253,7 @@ class HaScriptPicker extends LitElement { columns.actions = { title: "", - width: this.narrow ? undefined : "10%", + width: "64px", type: "overflow-menu", template: (script) => html` this._showInfo(script), }, + { + path: mdiTag, + label: this.hass.localize( + `ui.panel.config.script.picker.${script.category ? "edit_category" : "assign_category"}` + ), + action: () => this._editCategory(script), + }, { path: mdiPlay, label: this.hass.localize("ui.panel.config.script.picker.run"), @@ -236,6 +314,21 @@ class HaScriptPicker extends LitElement { } ); + protected hassSubscribe(): (UnsubscribeFunc | Promise)[] { + return [ + subscribeCategoryRegistry( + this.hass.connection, + "script", + (categories) => { + this._categories = categories; + } + ), + subscribeLabelRegistry(this.hass.connection, (labels) => { + this._labels = labels; + }), + ]; + } + protected render(): TemplateResult { return html` filter.value?.length + ).length} + .columns=${this._columns( + this.narrow, + this.hass.localize, + this.hass.locale + )} + .data=${this._scripts( + this.scripts, + this._entityReg, + this._categories, + this._labels, + this._filteredScripts + )} .empty=${!this.scripts.length} .activeFilters=${this._activeFilters} id="entity_id" @@ -255,6 +363,7 @@ class HaScriptPicker extends LitElement { @clear-filter=${this._clearFilter} hasFab clickable + class=${this.narrow ? "narrow" : ""} @row-click=${this._handleRowClicked} > + + + + + + ${!this.scripts.length ? html`
@@ -303,6 +471,95 @@ class HaScriptPicker extends LitElement { `; } + private _filterExpanded(ev) { + if (ev.detail.expanded) { + this._expandedFilter = ev.target.localName; + } else if (this._expandedFilter === ev.target.localName) { + this._expandedFilter = undefined; + } + } + + private _labelClicked = (ev: CustomEvent) => { + const label = ev.detail.label; + this._filters = { + ...this._filters, + "ha-filter-labels": { + value: [label.label_id], + items: undefined, + }, + }; + this._applyFilters(); + }; + + private _filterChanged(ev) { + const type = ev.target.localName; + this._filters[type] = ev.detail; + this._applyFilters(); + } + + private _clearFilter() { + this._filters = {}; + this._applyFilters(); + } + + private _applyFilters() { + const filters = Object.entries(this._filters); + let items: Set | undefined; + for (const [key, filter] of filters) { + if (filter.items) { + if (!items) { + items = filter.items; + continue; + } + items = + "intersection" in items + ? // @ts-ignore + items.intersection(filter.items) + : new Set([...items].filter((x) => filter.items!.has(x))); + } + if (key === "ha-filter-categories" && filter.value?.length) { + const categoryItems: Set = new Set(); + this.scripts + .filter( + (script) => + filter.value![0] === + this._entityReg.find((reg) => reg.entity_id === script.entity_id) + ?.categories.script + ) + .forEach((script) => categoryItems.add(script.entity_id)); + if (!items) { + items = categoryItems; + continue; + } + items = + "intersection" in items + ? // @ts-ignore + items.intersection(categoryItems) + : new Set([...items].filter((x) => categoryItems!.has(x))); + } + if (key === "ha-filter-labels" && filter.value?.length) { + const labelItems: Set = new Set(); + this.scripts + .filter((script) => + this._entityReg + .find((reg) => reg.entity_id === script.entity_id) + ?.labels.some((lbl) => filter.value!.includes(lbl)) + ) + .forEach((script) => labelItems.add(script.entity_id)); + if (!items) { + items = labelItems; + continue; + } + items = + "intersection" in items + ? // @ts-ignore + items.intersection(labelItems) + : new Set([...items].filter((x) => labelItems!.has(x))); + } + } + this._filteredScripts = items ? [...items] : undefined; + } + firstUpdated() { if (this._searchParms.has("blueprint")) { this._filterBlueprint(); @@ -314,28 +571,36 @@ class HaScriptPicker extends LitElement { if (!blueprint) { return; } - const [related, blueprints] = await Promise.all([ - findRelated(this.hass, "script_blueprint", blueprint), - fetchBlueprints(this.hass, "script"), - ]); - this._filteredScripts = related.script || []; - const blueprintMeta = blueprints[blueprint]; - this._activeFilters = [ - this.hass.localize( - "ui.panel.config.script.picker.filtered_by_blueprint", - { - name: - !blueprintMeta || "error" in blueprintMeta - ? blueprint - : blueprintMeta.metadata.name || blueprint, - } - ), - ]; + const related = await findRelated(this.hass, "script_blueprint", blueprint); + this._filters = { + ...this._filters, + "ha-filter-blueprints": { + value: [blueprint], + items: new Set(related.automation || []), + }, + }; + this._applyFilters(); } - private _clearFilter() { - this._filteredScripts = undefined; - this._activeFilters = undefined; + private _editCategory(script: any) { + const entityReg = this._entityReg.find( + (reg) => reg.entity_id === script.entity_id + ); + if (!entityReg) { + showAlertDialog(this, { + title: this.hass.localize( + "ui.panel.config.script.picker.no_category_support" + ), + text: this.hass.localize( + "ui.panel.config.script.picker.no_category_entity_reg" + ), + }); + return; + } + showAssignCategoryDialog(this, { + scope: "script", + entityReg, + }); } private _handleRowClicked(ev: HASSDomEvent) { @@ -477,6 +742,12 @@ class HaScriptPicker extends LitElement { return [ haStyle, css` + hass-tabs-subpage-data-table { + --data-table-row-height: 60px; + } + hass-tabs-subpage-data-table.narrow { + --data-table-row-height: 72px; + } a { text-decoration: none; } diff --git a/src/translations/en.json b/src/translations/en.json index 4a2a7b45fd..d1a4b1a6fb 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3551,8 +3551,13 @@ "filtered_by_blueprint": "[%key:ui::panel::config::automation::picker::filtered_by_blueprint%]", "headers": { "name": "Name", - "state": "State" + "state": "State", + "category": "Category" }, + "edit_category": "[%key:ui::panel::config::automation::picker::edit_category%]", + "assign_category": "[%key:ui::panel::config::automation::picker::assign_category%]", + "no_category_support": "You can't assign an category to this script", + "no_category_entity_reg": "To assign an category to an script it needs to have a unique ID.", "delete": "[%key:ui::common::delete%]", "duplicate": "[%key:ui::common::duplicate%]", "empty_header": "Create your first script", @@ -3655,8 +3660,13 @@ "headers": { "state": "State", "name": "Name", - "last_activated": "Last activated" + "last_activated": "Last activated", + "category": "Category" }, + "edit_category": "[%key:ui::panel::config::automation::picker::edit_category%]", + "assign_category": "[%key:ui::panel::config::automation::picker::assign_category%]", + "no_category_support": "You can't assign an category to this scene", + "no_category_entity_reg": "To assign an category to an scene it needs to have a unique ID.", "empty_header": "Create your first scene", "empty_text": "Scenes capture entities' states, so you can re-experience the same scene later on. For example, a ''Watching TV'' scene that dims the living room lights, sets a warm white color and turns on the TV." },