mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-26 18:56:39 +00:00
Card Picker: Entity Picker (#6693)
This commit is contained in:
parent
1431e75f8b
commit
f3639c2663
@ -1,4 +1,7 @@
|
|||||||
|
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||||
|
import "@material/mwc-tab/mwc-tab";
|
||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -11,30 +14,35 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
import { styleMap } from "lit-html/directives/style-map";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
import memoizeOne from "memoize-one";
|
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../common/search/search-input";
|
|
||||||
import { UNAVAILABLE_STATES } from "../../../../data/entity";
|
import { UNAVAILABLE_STATES } from "../../../../data/entity";
|
||||||
import { LovelaceCardConfig, LovelaceConfig } from "../../../../data/lovelace";
|
|
||||||
import {
|
import {
|
||||||
CustomCardEntry,
|
CustomCardEntry,
|
||||||
customCards,
|
customCards,
|
||||||
CUSTOM_TYPE_PREFIX,
|
CUSTOM_TYPE_PREFIX,
|
||||||
getCustomCardEntry,
|
getCustomCardEntry,
|
||||||
} from "../../../../data/lovelace_custom_cards";
|
} from "../../../../data/lovelace_custom_cards";
|
||||||
import { HomeAssistant } from "../../../../types";
|
|
||||||
import {
|
import {
|
||||||
calcUnusedEntities,
|
|
||||||
computeUsedEntities,
|
computeUsedEntities,
|
||||||
|
calcUnusedEntities,
|
||||||
} from "../../common/compute-unused-entities";
|
} from "../../common/compute-unused-entities";
|
||||||
import { tryCreateCardElement } from "../../create-element/create-card-element";
|
import { tryCreateCardElement } from "../../create-element/create-card-element";
|
||||||
import { LovelaceCard } from "../../types";
|
|
||||||
import { getCardStubConfig } from "../get-card-stub-config";
|
import { getCardStubConfig } from "../get-card-stub-config";
|
||||||
import { CardPickTarget, Card } from "../types";
|
|
||||||
import { coreCards } from "../lovelace-cards";
|
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 "../../../../components/ha-circular-progress";
|
||||||
|
import "../../../../common/search/search-input";
|
||||||
|
|
||||||
interface CardElement {
|
interface CardElement {
|
||||||
card: Card;
|
card: Card;
|
||||||
@ -53,14 +61,14 @@ export class HuiCardPicker extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _filter = "";
|
@internalProperty() private _filter = "";
|
||||||
|
|
||||||
private _unusedEntities?: string[];
|
|
||||||
|
|
||||||
private _usedEntities?: string[];
|
|
||||||
|
|
||||||
@internalProperty() private _width?: number;
|
@internalProperty() private _width?: number;
|
||||||
|
|
||||||
@internalProperty() private _height?: number;
|
@internalProperty() private _height?: number;
|
||||||
|
|
||||||
|
private _unusedEntities?: string[];
|
||||||
|
|
||||||
|
private _usedEntities?: string[];
|
||||||
|
|
||||||
private _filterCards = memoizeOne(
|
private _filterCards = memoizeOne(
|
||||||
(cardElements: CardElement[], filter?: string): CardElement[] => {
|
(cardElements: CardElement[], filter?: string): CardElement[] => {
|
||||||
if (!filter) {
|
if (!filter) {
|
||||||
@ -232,85 +240,6 @@ export class HuiCardPicker extends LitElement {
|
|||||||
this._filter = value;
|
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 {
|
private _cardPicked(ev: Event): void {
|
||||||
const config: LovelaceCardConfig = (ev.currentTarget! as CardPickTarget)
|
const config: LovelaceCardConfig = (ev.currentTarget! as CardPickTarget)
|
||||||
.config;
|
.config;
|
||||||
@ -406,6 +335,85 @@ export class HuiCardPicker extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
declare global {
|
||||||
|
278
src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts
Normal file
278
src/panels/lovelace/editor/card-editor/hui-dialog-create-card.ts
Normal file
@ -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<void> {
|
||||||
|
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`
|
||||||
|
<ha-dialog
|
||||||
|
open
|
||||||
|
scrimClickAction
|
||||||
|
@keydown=${this._ignoreKeydown}
|
||||||
|
@closed=${this._cancel}
|
||||||
|
.heading=${true}
|
||||||
|
>
|
||||||
|
<div slot="heading">
|
||||||
|
<ha-header-bar>
|
||||||
|
<div slot="title">
|
||||||
|
${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"
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-header-bar>
|
||||||
|
<mwc-tab-bar
|
||||||
|
.activeIndex=${this._currTabIndex}
|
||||||
|
@MDCTabBar:activated=${(ev: CustomEvent) =>
|
||||||
|
this._handleTabChanged(ev)}
|
||||||
|
>
|
||||||
|
<mwc-tab
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.cardpicker.by_card"
|
||||||
|
)}
|
||||||
|
></mwc-tab>
|
||||||
|
<mwc-tab
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.cardpicker.by_entity"
|
||||||
|
)}
|
||||||
|
></mwc-tab>
|
||||||
|
</mwc-tab-bar>
|
||||||
|
</div>
|
||||||
|
${this._currTabIndex === 0
|
||||||
|
? html`
|
||||||
|
<hui-card-picker
|
||||||
|
.lovelace=${this._params.lovelaceConfig}
|
||||||
|
.hass=${this.hass}
|
||||||
|
@config-changed=${this._handleCardPicked}
|
||||||
|
></hui-card-picker>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<div class="entity-picker-container">
|
||||||
|
<hui-entity-picker-table
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${true}
|
||||||
|
.entities=${this._allEntities(this.hass.states)}
|
||||||
|
@selected-changed=${this._handleSelectedChanged}
|
||||||
|
></hui-entity-picker-table>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
|
||||||
|
<div slot="primaryAction">
|
||||||
|
<mwc-button @click=${this._cancel}>
|
||||||
|
${this.hass!.localize("ui.common.cancel")}
|
||||||
|
</mwc-button>
|
||||||
|
${this._selectedEntities.length
|
||||||
|
? html`
|
||||||
|
<mwc-button @click=${this._suggestCards}>
|
||||||
|
${this.hass!.localize("ui.common.continue")}
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -11,28 +11,32 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
|
import { mdiHelpCircle } from "@mdi/js";
|
||||||
import "../../../../components/ha-dialog";
|
|
||||||
|
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 {
|
import type {
|
||||||
LovelaceCardConfig,
|
LovelaceCardConfig,
|
||||||
LovelaceViewConfig,
|
LovelaceViewConfig,
|
||||||
} from "../../../../data/lovelace";
|
} 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 "./hui-card-editor";
|
||||||
import type { ConfigChangedEvent, HuiCardEditor } from "./hui-card-editor";
|
|
||||||
import "./hui-card-picker";
|
|
||||||
import "./hui-card-preview";
|
import "./hui-card-preview";
|
||||||
import type { EditCardDialogParams } from "./show-edit-card-dialog";
|
import "../../../../components/ha-dialog";
|
||||||
import { getCardDocumentationURL } from "../get-card-documentation-url";
|
import "../../../../components/ha-header-bar";
|
||||||
import { mdiHelpCircle } from "@mdi/js";
|
import "../../../../components/ha-circular-progress";
|
||||||
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
|
|
||||||
import { HassDialog } from "../../../../dialogs/make-dialog-manager";
|
|
||||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -47,7 +51,7 @@ declare global {
|
|||||||
|
|
||||||
@customElement("hui-dialog-edit-card")
|
@customElement("hui-dialog-edit-card")
|
||||||
export class HuiDialogEditCard extends LitElement implements HassDialog {
|
export class HuiDialogEditCard extends LitElement implements HassDialog {
|
||||||
@property() protected hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@internalProperty() private _params?: EditCardDialogParams;
|
@internalProperty() private _params?: EditCardDialogParams;
|
||||||
|
|
||||||
@ -150,62 +154,56 @@ export class HuiDialogEditCard extends LitElement implements HassDialog {
|
|||||||
@keydown=${this._ignoreKeydown}
|
@keydown=${this._ignoreKeydown}
|
||||||
@closed=${this._cancel}
|
@closed=${this._cancel}
|
||||||
@opened=${this._opened}
|
@opened=${this._opened}
|
||||||
.heading=${html`${heading}
|
.heading=${true}
|
||||||
${this._documentationURL !== undefined
|
|
||||||
? html`
|
|
||||||
<a
|
|
||||||
class="header_button"
|
|
||||||
href=${this._documentationURL}
|
|
||||||
title=${this.hass!.localize("ui.panel.lovelace.menu.help")}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
dir=${computeRTLDirection(this.hass)}
|
|
||||||
>
|
|
||||||
<mwc-icon-button>
|
|
||||||
<ha-svg-icon path=${mdiHelpCircle}></ha-svg-icon>
|
|
||||||
</mwc-icon-button>
|
|
||||||
</a>
|
|
||||||
`
|
|
||||||
: ""}`}
|
|
||||||
>
|
>
|
||||||
<div>
|
<div slot="heading">
|
||||||
${this._cardConfig === undefined
|
<ha-header-bar>
|
||||||
? html`
|
<div slot="title">${heading}</div>
|
||||||
<hui-card-picker
|
${this._documentationURL !== undefined
|
||||||
.lovelace=${this._params.lovelaceConfig}
|
? html`
|
||||||
.hass=${this.hass}
|
<a
|
||||||
@config-changed=${this._handleCardPicked}
|
slot="actionItems"
|
||||||
></hui-card-picker>
|
class="header_button"
|
||||||
`
|
href=${this._documentationURL}
|
||||||
: html`
|
title=${this.hass!.localize("ui.panel.lovelace.menu.help")}
|
||||||
<div class="content">
|
target="_blank"
|
||||||
<div class="element-editor">
|
rel="noreferrer"
|
||||||
<hui-card-editor
|
dir=${computeRTLDirection(this.hass)}
|
||||||
.hass=${this.hass}
|
>
|
||||||
.lovelace=${this._params.lovelaceConfig}
|
<mwc-icon-button>
|
||||||
.value=${this._cardConfig}
|
<ha-svg-icon path=${mdiHelpCircle}></ha-svg-icon>
|
||||||
@config-changed=${this._handleConfigChanged}
|
</mwc-icon-button>
|
||||||
@GUImode-changed=${this._handleGUIModeChanged}
|
</a>
|
||||||
@editor-save=${this._save}
|
`
|
||||||
></hui-card-editor>
|
: ""}
|
||||||
</div>
|
</ha-header-bar>
|
||||||
<div class="element-preview">
|
</div>
|
||||||
<hui-card-preview
|
<div class="content">
|
||||||
.hass=${this.hass}
|
<div class="element-editor">
|
||||||
.config=${this._cardConfig}
|
<hui-card-editor
|
||||||
class=${this._error ? "blur" : ""}
|
.hass=${this.hass}
|
||||||
></hui-card-preview>
|
.lovelace=${this._params.lovelaceConfig}
|
||||||
${this._error
|
.value=${this._cardConfig}
|
||||||
? html`
|
@config-changed=${this._handleConfigChanged}
|
||||||
<ha-circular-progress
|
@GUImode-changed=${this._handleGUIModeChanged}
|
||||||
active
|
@editor-save=${this._save}
|
||||||
alt="Can't update card"
|
></hui-card-editor>
|
||||||
></ha-circular-progress>
|
</div>
|
||||||
`
|
<div class="element-preview">
|
||||||
: ``}
|
<hui-card-preview
|
||||||
</div>
|
.hass=${this.hass}
|
||||||
</div>
|
.config=${this._cardConfig}
|
||||||
`}
|
class=${this._error ? "blur" : ""}
|
||||||
|
></hui-card-preview>
|
||||||
|
${this._error
|
||||||
|
? html`
|
||||||
|
<ha-circular-progress
|
||||||
|
active
|
||||||
|
alt="Can't update card"
|
||||||
|
></ha-circular-progress>
|
||||||
|
`
|
||||||
|
: ``}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this._cardConfig !== undefined
|
${this._cardConfig !== undefined
|
||||||
? html`
|
? html`
|
||||||
@ -256,126 +254,6 @@ export class HuiDialogEditCard extends LitElement implements HassDialog {
|
|||||||
ev.stopPropagation();
|
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<ConfigChangedEvent>) {
|
private _handleConfigChanged(ev: HASSDomEvent<ConfigChangedEvent>) {
|
||||||
this._cardConfig = deepFreeze(ev.detail.config);
|
this._cardConfig = deepFreeze(ev.detail.config);
|
||||||
this._error = ev.detail.error;
|
this._error = ev.detail.error;
|
||||||
@ -463,6 +341,124 @@ export class HuiDialogEditCard extends LitElement implements HassDialog {
|
|||||||
showSaveSuccessToast(this, this.hass);
|
showSaveSuccessToast(this, this.hass);
|
||||||
this.closeDialog();
|
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 {
|
declare global {
|
||||||
|
@ -5,9 +5,9 @@ import {
|
|||||||
CSSResultArray,
|
CSSResultArray,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
query,
|
query,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
@ -21,7 +21,7 @@ import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
|
|||||||
import { computeCards } from "../../common/generate-lovelace-config";
|
import { computeCards } from "../../common/generate-lovelace-config";
|
||||||
import { addCards } from "../config-util";
|
import { addCards } from "../config-util";
|
||||||
import "./hui-card-preview";
|
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";
|
import { SuggestCardDialogParams } from "./show-suggest-card-dialog";
|
||||||
|
|
||||||
@customElement("hui-dialog-suggest-card")
|
@customElement("hui-dialog-suggest-card")
|
||||||
@ -179,7 +179,8 @@ export class HuiDialogSuggestCard extends LitElement {
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showEditCardDialog(this, {
|
|
||||||
|
showCreateCardDialog(this, {
|
||||||
lovelaceConfig: this._params!.lovelaceConfig,
|
lovelaceConfig: this._params!.lovelaceConfig,
|
||||||
saveConfig: this._params!.saveConfig,
|
saveConfig: this._params!.saveConfig,
|
||||||
path: this._params!.path,
|
path: this._params!.path,
|
||||||
|
@ -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`
|
||||||
|
<ha-data-table
|
||||||
|
auto-height
|
||||||
|
selectable
|
||||||
|
.id=${"entity_id"}
|
||||||
|
.columns=${this._columns(this.narrow!)}
|
||||||
|
.data=${this.entities}
|
||||||
|
.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"
|
||||||
|
)}
|
||||||
|
@selection-changed=${this._handleSelectionChanged}
|
||||||
|
></ha-data-table>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _columns = memoizeOne((narrow: boolean) => {
|
||||||
|
const columns: DataTableColumnContainer = {
|
||||||
|
icon: {
|
||||||
|
title: "",
|
||||||
|
type: "icon",
|
||||||
|
template: (_icon, entity: any) => html`
|
||||||
|
<state-badge
|
||||||
|
@click=${this._handleEntityClicked}
|
||||||
|
.hass=${this.hass!}
|
||||||
|
.stateObj=${entity.stateObj}
|
||||||
|
></state-badge>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity"),
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
grows: true,
|
||||||
|
direction: "asc",
|
||||||
|
template: (name, entity: any) => html`
|
||||||
|
<div @click=${this._handleEntityClicked} style="cursor: pointer;">
|
||||||
|
${name}
|
||||||
|
${narrow
|
||||||
|
? html`
|
||||||
|
<div class="secondary">
|
||||||
|
${entity.stateObj.entity_id}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
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`
|
||||||
|
<ha-relative-time
|
||||||
|
.hass=${this.hass!}
|
||||||
|
.datetime=${lastChanged}
|
||||||
|
></ha-relative-time>
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
});
|
||||||
|
|
||||||
|
private _handleSelectionChanged(
|
||||||
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
|
): 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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,
|
||||||
|
});
|
||||||
|
};
|
@ -10,39 +10,31 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import { mdiPlus } from "@mdi/js";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
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 { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../../common/entity/compute_state_name";
|
||||||
import {
|
import { computeRTL } from "../../../../common/util/compute_rtl";
|
||||||
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 { computeUnusedEntities } from "../../common/compute-unused-entities";
|
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 { showSuggestCardDialog } from "../card-editor/show-suggest-card-dialog";
|
||||||
import { showSelectViewDialog } from "../select-view/show-select-view-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")
|
@customElement("hui-unused-entities")
|
||||||
export class HuiUnusedEntities extends LitElement {
|
export class HuiUnusedEntities extends LitElement {
|
||||||
@property({ attribute: false }) public lovelace!: Lovelace;
|
@property({ attribute: false }) public lovelace!: Lovelace;
|
||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public narrow?: boolean;
|
@property({ type: Boolean }) public narrow?: boolean;
|
||||||
|
|
||||||
@internalProperty() private _unusedEntities: string[] = [];
|
@internalProperty() private _unusedEntities: string[] = [];
|
||||||
|
|
||||||
@ -52,74 +44,6 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
return this.lovelace.config;
|
return this.lovelace.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _columns = memoizeOne((narrow: boolean) => {
|
|
||||||
const columns: DataTableColumnContainer = {
|
|
||||||
icon: {
|
|
||||||
title: "",
|
|
||||||
type: "icon",
|
|
||||||
template: (_icon, entity: any) => html`
|
|
||||||
<state-badge
|
|
||||||
@click=${this._handleEntityClicked}
|
|
||||||
.hass=${this.hass!}
|
|
||||||
.stateObj=${entity.stateObj}
|
|
||||||
></state-badge>
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity"),
|
|
||||||
sortable: true,
|
|
||||||
filterable: true,
|
|
||||||
grows: true,
|
|
||||||
direction: "asc",
|
|
||||||
template: (name, entity: any) => html`
|
|
||||||
<div @click=${this._handleEntityClicked} style="cursor: pointer;">
|
|
||||||
${name}
|
|
||||||
${narrow
|
|
||||||
? html`
|
|
||||||
<div class="secondary">
|
|
||||||
${entity.stateObj.entity_id}
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
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`
|
|
||||||
<ha-relative-time
|
|
||||||
.hass=${this.hass!}
|
|
||||||
.datetime=${lastChanged}
|
|
||||||
></ha-relative-time>
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
|
|
||||||
return columns;
|
|
||||||
});
|
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues): void {
|
protected updated(changedProperties: PropertyValues): void {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
|
|
||||||
@ -161,9 +85,10 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-data-table
|
<hui-entity-picker-table
|
||||||
.columns=${this._columns(this.narrow!)}
|
.hass=${this.hass}
|
||||||
.data=${this._unusedEntities.map((entity) => {
|
.narrow=${this.narrow}
|
||||||
|
.entities=${this._unusedEntities.map((entity) => {
|
||||||
const stateObj = this.hass!.states[entity];
|
const stateObj = this.hass!.states[entity];
|
||||||
return {
|
return {
|
||||||
icon: "",
|
icon: "",
|
||||||
@ -173,18 +98,9 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
domain: computeDomain(entity),
|
domain: computeDomain(entity),
|
||||||
last_changed: stateObj!.last_changed,
|
last_changed: stateObj!.last_changed,
|
||||||
};
|
};
|
||||||
})}
|
}) as DataTableRowData[]}
|
||||||
.id=${"entity_id"}
|
@selected-changed=${this._handleSelectedChanged}
|
||||||
selectable
|
></hui-entity-picker-table>
|
||||||
@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"
|
|
||||||
)}
|
|
||||||
></ha-data-table>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="fab ${classMap({
|
class="fab ${classMap({
|
||||||
@ -211,19 +127,8 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
this._unusedEntities = [...unusedEntities].sort();
|
this._unusedEntities = [...unusedEntities].sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleSelectionChanged(
|
private _handleSelectedChanged(ev: CustomEvent): void {
|
||||||
ev: HASSDomEvent<SelectionChangedEvent>
|
this._selectedEntities = ev.detail.selectedEntities;
|
||||||
): void {
|
|
||||||
this._selectedEntities = ev.detail.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleEntityClicked(ev: Event) {
|
|
||||||
const entityId = ((ev.target as HTMLElement).closest(
|
|
||||||
".mdc-data-table__row"
|
|
||||||
) as any).rowId;
|
|
||||||
fireEvent(this, "hass-more-info", {
|
|
||||||
entityId,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addToLovelaceView(): void {
|
private _addToLovelaceView(): void {
|
||||||
@ -258,25 +163,22 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
/* min-height: calc(100vh - 112px); */
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
--ha-card-box-shadow: none;
|
--ha-card-box-shadow: none;
|
||||||
--ha-card-border-radius: 0;
|
--ha-card-border-radius: 0;
|
||||||
}
|
}
|
||||||
ha-data-table {
|
hui-entity-picker-table {
|
||||||
--data-table-border-width: 0;
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
margin-top: -20px;
|
margin-top: -20px;
|
||||||
}
|
}
|
||||||
.fab {
|
.fab {
|
||||||
overflow: hidden;
|
position: sticky;
|
||||||
position: absolute;
|
float: right;
|
||||||
right: 0;
|
right: calc(16px + env(safe-area-inset-right));
|
||||||
bottom: 0;
|
bottom: calc(16px + env(safe-area-inset-bottom));
|
||||||
padding: 16px;
|
|
||||||
padding-right: calc(16px + env(safe-area-inset-right));
|
|
||||||
padding-bottom: calc(16px + env(safe-area-inset-bottom));
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.fab.rtl {
|
.fab.rtl {
|
||||||
|
@ -23,11 +23,11 @@ import { computeCardSize } from "../common/compute-card-size";
|
|||||||
import { processConfigEntities } from "../common/process-config-entities";
|
import { processConfigEntities } from "../common/process-config-entities";
|
||||||
import { createBadgeElement } from "../create-element/create-badge-element";
|
import { createBadgeElement } from "../create-element/create-badge-element";
|
||||||
import { createCardElement } from "../create-element/create-card-element";
|
import { createCardElement } from "../create-element/create-card-element";
|
||||||
import { showEditCardDialog } from "../editor/card-editor/show-edit-card-dialog";
|
|
||||||
import { Lovelace, LovelaceBadge, LovelaceCard } from "../types";
|
import { Lovelace, LovelaceBadge, LovelaceCard } from "../types";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiPlus } from "@mdi/js";
|
||||||
import { nextRender } from "../../../common/util/render-status";
|
import { nextRender } from "../../../common/util/render-status";
|
||||||
|
import { showCreateCardDialog } from "../editor/card-editor/show-create-card-dialog";
|
||||||
|
|
||||||
let editCodeLoaded = false;
|
let editCodeLoaded = false;
|
||||||
|
|
||||||
@ -186,7 +186,7 @@ export class HUIView extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _addCard(): void {
|
private _addCard(): void {
|
||||||
showEditCardDialog(this, {
|
showCreateCardDialog(this, {
|
||||||
lovelaceConfig: this.lovelace!.config,
|
lovelaceConfig: this.lovelace!.config,
|
||||||
saveConfig: this.lovelace!.saveConfig,
|
saveConfig: this.lovelace!.saveConfig,
|
||||||
path: [this.index!],
|
path: [this.index!],
|
||||||
|
@ -260,6 +260,7 @@
|
|||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"and": "and",
|
"and": "and",
|
||||||
|
"continue": "Continue",
|
||||||
"previous": "Previous",
|
"previous": "Previous",
|
||||||
"loading": "Loading",
|
"loading": "Loading",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
@ -2351,7 +2352,11 @@
|
|||||||
},
|
},
|
||||||
"cardpicker": {
|
"cardpicker": {
|
||||||
"no_description": "No description available.",
|
"no_description": "No description available.",
|
||||||
"custom_card": "Custom"
|
"custom_card": "Custom",
|
||||||
|
"domain": "Domain",
|
||||||
|
"entity": "Entity",
|
||||||
|
"by_entity": "By Entity",
|
||||||
|
"by_card": "By Card"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user