From f3639c2663b70e76d1ccd4c084ab4d66f299cfa4 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Thu, 3 Sep 2020 18:28:53 -0500 Subject: [PATCH] Card Picker: Entity Picker (#6693) --- .../editor/card-editor/hui-card-picker.ts | 190 ++++----- .../card-editor/hui-dialog-create-card.ts | 278 +++++++++++++ .../card-editor/hui-dialog-edit-card.ts | 380 +++++++++--------- .../card-editor/hui-dialog-suggest-card.ts | 7 +- .../card-editor/hui-entity-picker-table.ts | 151 +++++++ .../card-editor/show-create-card-dialog.ts | 25 ++ .../unused-entities/hui-unused-entities.ts | 152 ++----- src/panels/lovelace/views/hui-view.ts | 4 +- src/translations/en.json | 7 +- 9 files changed, 780 insertions(+), 414 deletions(-) create mode 100644 src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts create mode 100644 src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts create mode 100644 src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts diff --git a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts index dce01a77c4..54e108a46f 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-picker.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-picker.ts @@ -1,4 +1,7 @@ +import "@material/mwc-tab-bar/mwc-tab-bar"; +import "@material/mwc-tab/mwc-tab"; import Fuse from "fuse.js"; +import memoizeOne from "memoize-one"; import { css, CSSResult, @@ -11,30 +14,35 @@ import { TemplateResult, } from "lit-element"; import { classMap } from "lit-html/directives/class-map"; +import { styleMap } from "lit-html/directives/style-map"; import { until } from "lit-html/directives/until"; -import memoizeOne from "memoize-one"; + import { fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../common/search/search-input"; import { UNAVAILABLE_STATES } from "../../../../data/entity"; -import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace"; import { CustomCardEntry, customCards, CUSTOM_TYPE_PREFIX, getCustomCardEntry, } from "../../../../data/lovelace_custom_cards"; -import { HomeAssistant } from "../../../../types"; import { - calcUnusedEntities, computeUsedEntities, + calcUnusedEntities, } from "../../common/compute-unused-entities"; import { tryCreateCardElement } from "../../create-element/create-card-element"; -import { LovelaceCard } from "../../types"; import { getCardStubConfig } from "../get-card-stub-config"; -import { CardPickTarget, Card } from "../types"; import { coreCards } from "../lovelace-cards"; -import { styleMap } from "lit-html/directives/style-map"; + +import type { CardPickTarget, Card } from "../types"; +import type { LovelaceCard } from "../../types"; +import type { HomeAssistant } from "../../../../types"; +import type { + LovelaceCardConfig, + LovelaceConfig, +} from "../../../../data/lovelace"; + import "../../../../components/ha-circular-progress"; +import "../../../../common/search/search-input"; interface CardElement { card: Card; @@ -53,14 +61,14 @@ export class HuiCardPicker extends LitElement { @internalProperty() private _filter = ""; - private _unusedEntities?: string[]; - - private _usedEntities?: string[]; - @internalProperty() private _width?: number; @internalProperty() private _height?: number; + private _unusedEntities?: string[]; + + private _usedEntities?: string[]; + private _filterCards = memoizeOne( (cardElements: CardElement[], filter?: string): CardElement[] => { if (!filter) { @@ -232,85 +240,6 @@ export class HuiCardPicker extends LitElement { this._filter = value; } - static get styles(): CSSResult[] { - return [ - css` - .cards-container { - display: grid; - grid-gap: 8px 8px; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - margin-top: 20px; - } - - .card { - height: 100%; - max-width: 500px; - display: flex; - flex-direction: column; - border-radius: 4px; - border: 1px solid var(--divider-color); - background: var(--primary-background-color, #fafafa); - cursor: pointer; - box-sizing: border-box; - position: relative; - } - - .card-header { - color: var(--ha-card-header-color, --primary-text-color); - font-family: var(--ha-card-header-font-family, inherit); - font-size: 16px; - font-weight: bold; - letter-spacing: -0.012em; - line-height: 20px; - padding: 12px 16px; - display: block; - text-align: center; - background: var( - --ha-card-background, - var(--card-background-color, white) - ); - border-radius: 0 0 4px 4px; - border-bottom: 1px solid var(--divider-color); - } - - .preview { - pointer-events: none; - margin: 20px; - flex-grow: 1; - display: flex; - align-items: center; - justify-content: center; - } - - .preview > :first-child { - zoom: 0.6; - display: block; - width: 100%; - } - - .description { - text-align: center; - } - - .spinner { - align-items: center; - justify-content: center; - } - - .overlay { - position: absolute; - width: 100%; - height: 100%; - z-index: 1; - } - - .manual { - max-width: none; - } - `, - ]; - } - private _cardPicked(ev: Event): void { const config: LovelaceCardConfig = (ev.currentTarget! as CardPickTarget) .config; @@ -406,6 +335,85 @@ export class HuiCardPicker extends LitElement { `; } + + static get styles(): CSSResult[] { + return [ + css` + .cards-container { + display: grid; + grid-gap: 8px 8px; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + margin-top: 20px; + } + + .card { + height: 100%; + max-width: 500px; + display: flex; + flex-direction: column; + border-radius: 4px; + border: 1px solid var(--divider-color); + background: var(--primary-background-color, #fafafa); + cursor: pointer; + box-sizing: border-box; + position: relative; + } + + .card-header { + color: var(--ha-card-header-color, --primary-text-color); + font-family: var(--ha-card-header-font-family, inherit); + font-size: 16px; + font-weight: bold; + letter-spacing: -0.012em; + line-height: 20px; + padding: 12px 16px; + display: block; + text-align: center; + background: var( + --ha-card-background, + var(--card-background-color, white) + ); + border-radius: 0 0 4px 4px; + border-bottom: 1px solid var(--divider-color); + } + + .preview { + pointer-events: none; + margin: 20px; + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + } + + .preview > :first-child { + zoom: 0.6; + display: block; + width: 100%; + } + + .description { + text-align: center; + } + + .spinner { + align-items: center; + justify-content: center; + } + + .overlay { + position: absolute; + width: 100%; + height: 100%; + z-index: 1; + } + + .manual { + max-width: none; + } + `, + ]; + } } declare global { diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts new file mode 100644 index 0000000000..c0fb354da9 --- /dev/null +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts @@ -0,0 +1,278 @@ +import "@material/mwc-tab-bar/mwc-tab-bar"; +import "@material/mwc-tab/mwc-tab"; +import { + css, + CSSResultArray, + customElement, + html, + internalProperty, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoize from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import { computeDomain } from "../../../../common/entity/compute_domain"; +import { computeStateName } from "../../../../common/entity/compute_state_name"; +import { DataTableRowData } from "../../../../components/data-table/ha-data-table"; +import "../../../../components/ha-dialog"; +import "../../../../components/ha-header-bar"; +import type { LovelaceViewConfig } from "../../../../data/lovelace"; +import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; +import { haStyleDialog } from "../../../../resources/styles"; +import type { HomeAssistant } from "../../../../types"; +import "./hui-card-picker"; +import "./hui-entity-picker-table"; +import { + EditCardDialogParams, + showEditCardDialog, +} from "./show-edit-card-dialog"; +import { showSuggestCardDialog } from "./show-suggest-card-dialog"; + +declare global { + interface HASSDomEvents { + "selected-changed": SelectedChangedEvent; + } +} + +interface SelectedChangedEvent { + selectedEntities: string[]; +} + +@customElement("hui-dialog-create-card") +export class HuiCreateDialogCard extends LitElement implements HassDialog { + @property({ attribute: false }) protected hass!: HomeAssistant; + + @internalProperty() private _params?: EditCardDialogParams; + + @internalProperty() private _viewConfig!: LovelaceViewConfig; + + @internalProperty() private _selectedEntities: string[] = []; + + @internalProperty() private _currTabIndex = 0; + + public async showDialog(params: EditCardDialogParams): Promise { + this._params = params; + const [view] = params.path; + this._viewConfig = params.lovelaceConfig.views[view]; + } + + public closeDialog(): boolean { + this._params = undefined; + this._currTabIndex = 0; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + return true; + } + + protected render(): TemplateResult { + if (!this._params) { + return html``; + } + + return html` + +
+ +
+ ${this._viewConfig.title + ? this.hass!.localize( + "ui.panel.lovelace.editor.edit_card.pick_card_view_title", + "name", + `"${this._viewConfig.title}"` + ) + : this.hass!.localize( + "ui.panel.lovelace.editor.edit_card.pick_card" + )} +
+
+ + this._handleTabChanged(ev)} + > + + + +
+ ${this._currTabIndex === 0 + ? html` + + ` + : html` +
+ +
+ `} + +
+ + ${this.hass!.localize("ui.common.cancel")} + + ${this._selectedEntities.length + ? html` + + ${this.hass!.localize("ui.common.continue")} + + ` + : ""} +
+
+ `; + } + + private _ignoreKeydown(ev: KeyboardEvent) { + ev.stopPropagation(); + } + + static get styles(): CSSResultArray { + return [ + haStyleDialog, + css` + @media all and (max-width: 450px), all and (max-height: 500px) { + /* overrule the ha-style-dialog max-height on small screens */ + ha-dialog { + --mdc-dialog-max-height: 100%; + height: 100%; + } + } + + @media all and (min-width: 850px) { + ha-dialog { + --mdc-dialog-min-width: 845px; + } + } + + ha-dialog { + --mdc-dialog-max-width: 845px; + } + + ha-header-bar { + --mdc-theme-on-primary: var(--primary-text-color); + --mdc-theme-primary: var(--mdc-theme-surface); + flex-shrink: 0; + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } + + @media (min-width: 1200px) { + ha-dialog { + --mdc-dialog-max-width: calc(100% - 32px); + --mdc-dialog-min-width: 1000px; + } + } + + .header_button { + color: inherit; + text-decoration: none; + } + + mwc-tab-bar { + padding-top: 8px; + } + + .entity-picker-container { + display: flex; + flex-direction: column; + height: 100%; + min-height: calc(100vh - 112px); + margin-top: -20px; + } + `, + ]; + } + + private _handleCardPicked(ev) { + const config = ev.detail.config; + if (this._params!.entities && this._params!.entities.length) { + if (Object.keys(config).includes("entities")) { + config.entities = this._params!.entities; + } else if (Object.keys(config).includes("entity")) { + config.entity = this._params!.entities[0]; + } + } + + showEditCardDialog(this, { + lovelaceConfig: this._params!.lovelaceConfig, + saveConfig: this._params!.saveConfig, + path: this._params!.path, + cardConfig: config, + }); + + this.closeDialog(); + } + + private _handleTabChanged(ev: CustomEvent): void { + const newTab = ev.detail.index; + if (newTab === this._currTabIndex) { + return; + } + + this._currTabIndex = ev.detail.index; + this._selectedEntities = []; + } + + private _handleSelectedChanged(ev: CustomEvent): void { + this._selectedEntities = ev.detail.selectedEntities; + } + + private _cancel(ev?: Event) { + if (ev) { + ev.stopPropagation(); + } + this.closeDialog(); + } + + private _suggestCards(): void { + showSuggestCardDialog(this, { + lovelaceConfig: this._params!.lovelaceConfig, + saveConfig: this._params!.saveConfig, + path: this._params!.path as [number], + entities: this._selectedEntities, + }); + + this.closeDialog(); + } + + private _allEntities = memoize((entities) => + Object.keys(entities).map((entity) => { + const stateObj = this.hass.states[entity]; + return { + icon: "", + entity_id: entity, + stateObj, + name: computeStateName(stateObj), + domain: computeDomain(entity), + last_changed: stateObj!.last_changed, + } as DataTableRowData; + }) + ); +} + +declare global { + interface HTMLElementTagNameMap { + "hui-dialog-create-card": HuiCreateDialogCard; + } +} diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index effc794665..c8caba2a48 100755 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -11,28 +11,32 @@ import { TemplateResult, PropertyValues, } from "lit-element"; -import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event"; -import "../../../../components/ha-dialog"; +import { mdiHelpCircle } from "@mdi/js"; + +import { fireEvent } from "../../../../common/dom/fire_event"; +import { haStyleDialog } from "../../../../resources/styles"; +import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; +import { addCard, replaceCard } from "../config-util"; +import { getCardDocumentationURL } from "../get-card-documentation-url"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; +import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; + +import type { HomeAssistant } from "../../../../types"; +import type { GUIModeChangedEvent } from "../types"; +import type { ConfigChangedEvent, HuiCardEditor } from "./hui-card-editor"; +import type { EditCardDialogParams } from "./show-edit-card-dialog"; +import type { HassDialog } from "../../../../dialogs/make-dialog-manager"; +import type { HASSDomEvent } from "../../../../common/dom/fire_event"; import type { LovelaceCardConfig, LovelaceViewConfig, } from "../../../../data/lovelace"; -import { haStyleDialog } from "../../../../resources/styles"; -import "../../../../components/ha-circular-progress"; -import type { HomeAssistant } from "../../../../types"; -import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; -import { addCard, replaceCard } from "../config-util"; -import type { GUIModeChangedEvent } from "../types"; + import "./hui-card-editor"; -import type { ConfigChangedEvent, HuiCardEditor } from "./hui-card-editor"; -import "./hui-card-picker"; import "./hui-card-preview"; -import type { EditCardDialogParams } from "./show-edit-card-dialog"; -import { getCardDocumentationURL } from "../get-card-documentation-url"; -import { mdiHelpCircle } from "@mdi/js"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; -import { HassDialog } from "../../../../dialogs/make-dialog-manager"; -import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box"; +import "../../../../components/ha-dialog"; +import "../../../../components/ha-header-bar"; +import "../../../../components/ha-circular-progress"; declare global { // for fire event @@ -47,7 +51,7 @@ declare global { @customElement("hui-dialog-edit-card") export class HuiDialogEditCard extends LitElement implements HassDialog { - @property() protected hass!: HomeAssistant; + @property({ attribute: false }) public hass!: HomeAssistant; @internalProperty() private _params?: EditCardDialogParams; @@ -150,62 +154,56 @@ export class HuiDialogEditCard extends LitElement implements HassDialog { @keydown=${this._ignoreKeydown} @closed=${this._cancel} @opened=${this._opened} - .heading=${html`${heading} - ${this._documentationURL !== undefined - ? html` - - - - - - ` - : ""}`} + .heading=${true} > -
- ${this._cardConfig === undefined - ? html` - - ` - : html` -
-
- -
-
- - ${this._error - ? html` - - ` - : ``} -
-
- `} +
+ +
${heading}
+ ${this._documentationURL !== undefined + ? html` + + + + + + ` + : ""} +
+
+
+
+ +
+
+ + ${this._error + ? html` + + ` + : ``} +
${this._cardConfig !== undefined ? html` @@ -256,126 +254,6 @@ export class HuiDialogEditCard extends LitElement implements HassDialog { ev.stopPropagation(); } - static get styles(): CSSResultArray { - return [ - haStyleDialog, - css` - :host { - --code-mirror-max-height: calc(100vh - 176px); - } - - @media all and (max-width: 450px), all and (max-height: 500px) { - /* overrule the ha-style-dialog max-height on small screens */ - ha-dialog { - --mdc-dialog-max-height: 100%; - height: 100%; - } - } - - @media all and (min-width: 850px) { - ha-dialog { - --mdc-dialog-min-width: 845px; - } - } - - ha-dialog { - --mdc-dialog-max-width: 845px; - } - - .center { - margin-left: auto; - margin-right: auto; - } - - .content { - display: flex; - flex-direction: column; - margin: 0 -10px; - } - .content hui-card-preview { - margin: 4px auto; - max-width: 390px; - } - .content .element-editor { - margin: 0 10px; - } - - @media (min-width: 1200px) { - ha-dialog { - --mdc-dialog-max-width: calc(100% - 32px); - --mdc-dialog-min-width: 1000px; - } - - .content { - flex-direction: row; - } - .content > * { - flex-basis: 0; - flex-grow: 1; - flex-shrink: 1; - min-width: 0; - } - .content hui-card-preview { - padding: 8px 10px; - margin: auto 0px; - max-width: 500px; - } - } - - mwc-button ha-circular-progress { - margin-right: 20px; - } - .hidden { - display: none; - } - .element-editor { - margin-bottom: 8px; - } - .blur { - filter: blur(2px) grayscale(100%); - } - .element-preview { - position: relative; - } - .element-preview ha-circular-progress { - top: 50%; - left: 50%; - position: absolute; - z-index: 10; - } - hui-card-preview { - padding-top: 8px; - margin-bottom: 4px; - display: block; - width: 100%; - box-sizing: border-box; - } - .gui-mode-button { - margin-right: auto; - } - .header { - display: flex; - align-items: center; - justify-content: space-between; - } - `, - ]; - } - - private _handleCardPicked(ev) { - const config = ev.detail.config; - if (this._params!.entities && this._params!.entities.length) { - if (Object.keys(config).includes("entities")) { - config.entities = this._params!.entities; - } else if (Object.keys(config).includes("entity")) { - config.entity = this._params!.entities[0]; - } - } - this._cardConfig = deepFreeze(config); - this._error = ev.detail.error; - this._dirty = true; - } - private _handleConfigChanged(ev: HASSDomEvent) { this._cardConfig = deepFreeze(ev.detail.config); this._error = ev.detail.error; @@ -463,6 +341,124 @@ export class HuiDialogEditCard extends LitElement implements HassDialog { showSaveSuccessToast(this, this.hass); this.closeDialog(); } + + static get styles(): CSSResultArray { + return [ + haStyleDialog, + css` + :host { + --code-mirror-max-height: calc(100vh - 176px); + } + + @media all and (max-width: 450px), all and (max-height: 500px) { + /* overrule the ha-style-dialog max-height on small screens */ + ha-dialog { + --mdc-dialog-max-height: 100%; + height: 100%; + } + } + + @media all and (min-width: 850px) { + ha-dialog { + --mdc-dialog-min-width: 845px; + } + } + + ha-dialog { + --mdc-dialog-max-width: 845px; + } + + ha-header-bar { + --mdc-theme-on-primary: var(--primary-text-color); + --mdc-theme-primary: var(--mdc-theme-surface); + flex-shrink: 0; + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } + + .center { + margin-left: auto; + margin-right: auto; + } + + .content { + display: flex; + flex-direction: column; + margin: 0 -10px; + } + .content hui-card-preview { + margin: 4px auto; + max-width: 390px; + } + .content .element-editor { + margin: 0 10px; + } + + @media (min-width: 1200px) { + ha-dialog { + --mdc-dialog-max-width: calc(100% - 32px); + --mdc-dialog-min-width: 1000px; + } + + .content { + flex-direction: row; + } + .content > * { + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + min-width: 0; + } + .content hui-card-preview { + padding: 8px 10px; + margin: auto 0px; + max-width: 500px; + } + } + + mwc-button ha-circular-progress { + margin-right: 20px; + } + .hidden { + display: none; + } + .element-editor { + margin-bottom: 8px; + } + .blur { + filter: blur(2px) grayscale(100%); + } + .element-preview { + position: relative; + } + .element-preview ha-circular-progress { + top: 50%; + left: 50%; + position: absolute; + z-index: 10; + } + hui-card-preview { + padding-top: 8px; + margin-bottom: 4px; + display: block; + width: 100%; + box-sizing: border-box; + } + .gui-mode-button { + margin-right: auto; + } + .header { + display: flex; + align-items: center; + justify-content: space-between; + } + .header_button { + color: inherit; + text-decoration: none; + } + `, + ]; + } } declare global { diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts index a4f222200c..00e7d8b5a0 100755 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-suggest-card.ts @@ -5,9 +5,9 @@ import { CSSResultArray, customElement, html, + internalProperty, LitElement, property, - internalProperty, query, TemplateResult, } from "lit-element"; @@ -21,7 +21,7 @@ import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; import { computeCards } from "../../common/generate-lovelace-config"; import { addCards } from "../config-util"; import "./hui-card-preview"; -import { showEditCardDialog } from "./show-edit-card-dialog"; +import { showCreateCardDialog } from "./show-create-card-dialog"; import { SuggestCardDialogParams } from "./show-suggest-card-dialog"; @customElement("hui-dialog-suggest-card") @@ -179,7 +179,8 @@ export class HuiDialogSuggestCard extends LitElement { ) { return; } - showEditCardDialog(this, { + + showCreateCardDialog(this, { lovelaceConfig: this._params!.lovelaceConfig, saveConfig: this._params!.saveConfig, path: this._params!.path, diff --git a/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts b/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts new file mode 100644 index 0000000000..3a724fc6e2 --- /dev/null +++ b/src/panels/lovelace/editor/card-editor/hui-entity-picker-table.ts @@ -0,0 +1,151 @@ +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + TemplateResult, +} from "lit-element"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import type { HASSDomEvent } from "../../../../common/dom/fire_event"; +import { computeRTLDirection } from "../../../../common/util/compute_rtl"; +import "../../../../components/data-table/ha-data-table"; +import type { + DataTableColumnContainer, + DataTableRowData, + SelectionChangedEvent, +} from "../../../../components/data-table/ha-data-table"; +import "../../../../components/entity/state-badge"; +import "../../../../components/ha-relative-time"; +import type { HomeAssistant } from "../../../../types"; + +@customElement("hui-entity-picker-table") +export class HuiEntityPickerTable extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public narrow?: boolean; + + @property({ type: Array }) public entities!: DataTableRowData[]; + + protected render(): TemplateResult { + return html` + + `; + } + + private _columns = memoizeOne((narrow: boolean) => { + const columns: DataTableColumnContainer = { + icon: { + title: "", + type: "icon", + template: (_icon, entity: any) => html` + + `, + }, + name: { + title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity"), + sortable: true, + filterable: true, + grows: true, + direction: "asc", + template: (name, entity: any) => html` +
+ ${name} + ${narrow + ? html` +
+ ${entity.stateObj.entity_id} +
+ ` + : ""} +
+ `, + }, + }; + + columns.entity_id = { + title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity_id"), + sortable: true, + filterable: true, + width: "30%", + hidden: narrow, + }; + + columns.domain = { + title: this.hass!.localize("ui.panel.lovelace.unused_entities.domain"), + sortable: true, + filterable: true, + width: "15%", + hidden: narrow, + }; + + columns.last_changed = { + title: this.hass!.localize( + "ui.panel.lovelace.unused_entities.last_changed" + ), + type: "numeric", + sortable: true, + width: "15%", + hidden: narrow, + template: (lastChanged: string) => html` + + `, + }; + + return columns; + }); + + private _handleSelectionChanged( + ev: HASSDomEvent + ): void { + const selectedEntities = ev.detail.value; + + fireEvent(this, "selected-changed", { selectedEntities }); + } + + private _handleEntityClicked(ev: Event) { + const entityId = ((ev.target as HTMLElement).closest( + ".mdc-data-table__row" + ) as any).rowId; + fireEvent(this, "hass-more-info", { + entityId, + }); + } + + static get styles(): CSSResult { + return css` + ha-data-table { + --data-table-border-width: 0; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-entity-picker-table": HuiEntityPickerTable; + } +} diff --git a/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts b/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts new file mode 100644 index 0000000000..42192a467e --- /dev/null +++ b/src/panels/lovelace/editor/card-editor/show-create-card-dialog.ts @@ -0,0 +1,25 @@ +import { fireEvent } from "../../../../common/dom/fire_event"; +import { LovelaceConfig } from "../../../../data/lovelace"; + +export interface CreateCardDialogParams { + lovelaceConfig: LovelaceConfig; + saveConfig: (config: LovelaceConfig) => void; + path: [number] | [number, number]; + entities?: string[]; // We can pass entity id's that will be added to the config when a card is picked +} + +const importCreateCardDialog = () => + import( + /* webpackChunkName: "hui-dialog-create-card" */ "./hui-dialog-create-card" + ); + +export const showCreateCardDialog = ( + element: HTMLElement, + createCardDialogParams: CreateCardDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "hui-dialog-create-card", + dialogImport: importCreateCardDialog, + dialogParams: createCardDialogParams, + }); +}; diff --git a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts index fdc35e7d84..1e81f187a5 100644 --- a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts +++ b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts @@ -10,39 +10,31 @@ import { PropertyValues, TemplateResult, } from "lit-element"; +import { mdiPlus } from "@mdi/js"; import { classMap } from "lit-html/directives/class-map"; -import memoizeOne from "memoize-one"; -import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; + import { computeDomain } from "../../../../common/entity/compute_domain"; import { computeStateName } from "../../../../common/entity/compute_state_name"; -import { - computeRTL, - computeRTLDirection, -} from "../../../../common/util/compute_rtl"; -import "../../../../components/data-table/ha-data-table"; -import type { - DataTableColumnContainer, - SelectionChangedEvent, -} from "../../../../components/data-table/ha-data-table"; -import "../../../../components/entity/state-badge"; -import "../../../../components/ha-icon"; -import "../../../../components/ha-relative-time"; -import type { LovelaceConfig } from "../../../../data/lovelace"; -import type { HomeAssistant } from "../../../../types"; +import { computeRTL } from "../../../../common/util/compute_rtl"; import { computeUnusedEntities } from "../../common/compute-unused-entities"; -import type { Lovelace } from "../../types"; -import "../../../../components/ha-svg-icon"; -import { mdiPlus } from "@mdi/js"; import { showSuggestCardDialog } from "../card-editor/show-suggest-card-dialog"; import { showSelectViewDialog } from "../select-view/show-select-view-dialog"; +import type { DataTableRowData } from "../../../../components/data-table/ha-data-table"; +import type { LovelaceConfig } from "../../../../data/lovelace"; +import type { HomeAssistant } from "../../../../types"; +import type { Lovelace } from "../../types"; + +import "../card-editor/hui-entity-picker-table"; +import "../../../../components/ha-svg-icon"; + @customElement("hui-unused-entities") export class HuiUnusedEntities extends LitElement { @property({ attribute: false }) public lovelace!: Lovelace; @property({ attribute: false }) public hass!: HomeAssistant; - @property() public narrow?: boolean; + @property({ type: Boolean }) public narrow?: boolean; @internalProperty() private _unusedEntities: string[] = []; @@ -52,74 +44,6 @@ export class HuiUnusedEntities extends LitElement { return this.lovelace.config; } - private _columns = memoizeOne((narrow: boolean) => { - const columns: DataTableColumnContainer = { - icon: { - title: "", - type: "icon", - template: (_icon, entity: any) => html` - - `, - }, - name: { - title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity"), - sortable: true, - filterable: true, - grows: true, - direction: "asc", - template: (name, entity: any) => html` -
- ${name} - ${narrow - ? html` -
- ${entity.stateObj.entity_id} -
- ` - : ""} -
- `, - }, - }; - - if (narrow) { - return columns; - } - - columns.entity_id = { - title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity_id"), - sortable: true, - filterable: true, - width: "30%", - }; - columns.domain = { - title: this.hass!.localize("ui.panel.lovelace.unused_entities.domain"), - sortable: true, - filterable: true, - width: "15%", - }; - columns.last_changed = { - title: this.hass!.localize( - "ui.panel.lovelace.unused_entities.last_changed" - ), - type: "numeric", - sortable: true, - width: "15%", - template: (lastChanged: string) => html` - - `, - }; - - return columns; - }); - protected updated(changedProperties: PropertyValues): void { super.updated(changedProperties); @@ -161,9 +85,10 @@ export class HuiUnusedEntities extends LitElement { ` : ""} - { + { const stateObj = this.hass!.states[entity]; return { icon: "", @@ -173,18 +98,9 @@ export class HuiUnusedEntities extends LitElement { domain: computeDomain(entity), last_changed: stateObj!.last_changed, }; - })} - .id=${"entity_id"} - selectable - @selection-changed=${this._handleSelectionChanged} - .dir=${computeRTLDirection(this.hass)} - .searchLabel=${this.hass.localize( - "ui.panel.lovelace.unused_entities.search" - )} - .noDataText=${this.hass.localize( - "ui.panel.lovelace.unused_entities.no_data" - )} - > + }) as DataTableRowData[]} + @selected-changed=${this._handleSelectedChanged} + >