diff --git a/gallery/src/demos/demo-integration-card.ts b/gallery/src/demos/demo-integration-card.ts index 966978ffd1..e8c1694700 100644 --- a/gallery/src/demos/demo-integration-card.ts +++ b/gallery/src/demos/demo-integration-card.ts @@ -44,9 +44,10 @@ const createConfigEntry = ( const createManifest = ( isCustom: boolean, - isCloud: boolean + isCloud: boolean, + name = "ESPHome" ): IntegrationManifest => ({ - name: "ESPHome", + name, domain: "esphome", is_built_in: !isCustom, config_flow: false, @@ -103,7 +104,7 @@ const configFlows: DataEntryFlowProgressExtended[] = [ }, }, step_id: "discovery_confirm", - localized_title: "Roku: Living room Roku", + localized_title: "Living room Roku", }, { flow_id: "adbb401329d8439ebb78ef29837826a8", @@ -234,7 +235,8 @@ export class DemoIntegrationCard extends LitElement { .flow=${flow} .manifest=${createManifest( this.isCustomIntegration, - this.isCloud + this.isCloud, + flow.handler === "roku" ? "Roku" : "Philips Hue" )} > ` diff --git a/src/panels/config/integrations/ha-config-integrations-common.ts b/src/panels/config/integrations/ha-config-integrations-common.ts deleted file mode 100644 index f7e1117ceb..0000000000 --- a/src/panels/config/integrations/ha-config-integrations-common.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { mdiPackageVariant, mdiCloud } from "@mdi/js"; -import "@polymer/paper-tooltip/paper-tooltip"; -import { css, html } from "lit-element"; -import { IntegrationManifest } from "../../../data/integration"; -import { HomeAssistant } from "../../../types"; - -export const haConfigIntegrationsStyles = css` - .banner { - background-color: var(--state-color); - color: var(--text-on-state-color); - text-align: center; - padding: 8px; - } - .icons { - position: absolute; - top: 0px; - right: 16px; - color: var(--text-on-state-color, var(--secondary-text-color)); - background-color: var(--state-color, #e0e0e0); - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - padding: 1px 4px 2px; - } - .icons ha-svg-icon { - width: 20px; - height: 20px; - } - paper-tooltip { - white-space: nowrap; - } -`; - -export const haConfigIntegrationRenderIcons = ( - hass: HomeAssistant, - manifest?: IntegrationManifest -) => { - const icons: [string, string][] = []; - - if (manifest) { - if (!manifest.is_built_in) { - icons.push([ - mdiPackageVariant, - hass.localize( - "ui.panel.config.integrations.config_entry.provided_by_custom_integration" - ), - ]); - } - - if (manifest.iot_class && manifest.iot_class.startsWith("cloud_")) { - icons.push([ - mdiCloud, - hass.localize( - "ui.panel.config.integrations.config_entry.depends_on_cloud" - ), - ]); - } - } - - return icons.length === 0 - ? "" - : html` -
- ${icons.map( - ([icon, description]) => html` - - - ${description} - - ` - )} -
- `; -}; diff --git a/src/panels/config/integrations/ha-ignored-config-entry-card.ts b/src/panels/config/integrations/ha-ignored-config-entry-card.ts index e4332bd65e..cbd6c476b7 100644 --- a/src/panels/config/integrations/ha-ignored-config-entry-card.ts +++ b/src/panels/config/integrations/ha-ignored-config-entry-card.ts @@ -31,6 +31,7 @@ export class HaIgnoredConfigEntryCard extends LitElement { "ui.panel.config.integrations.ignore.ignored" )} .domain=${this.entry.domain} + .localizedDomainName=${this.entry.localized_domain_name} .label=${this.entry.title === "Ignored" ? // In 2020.2 we added support for entry.title. All ignored entries before // that have title "Ignored" so we fallback to localized domain name. @@ -38,7 +39,6 @@ export class HaIgnoredConfigEntryCard extends LitElement { : this.entry.title} > - -
- ${haConfigIntegrationRenderIcons(this.hass, this.manifest)} -
- -
-

${this.label}

-
+ +
`; } - private _onImageLoad(ev) { - ev.target.style.visibility = "initial"; - } - - private _onImageError(ev) { - ev.target.style.visibility = "hidden"; - } - - static get styles(): CSSResult[] { - return [ - haConfigIntegrationsStyles, - css` - ha-card { - display: flex; - flex-direction: column; - height: 100%; - --ha-card-border-color: var(--state-color); - --mdc-theme-primary: var(--state-color); - } - .content { - position: relative; - flex: 1; - } - .image { - height: 60px; - margin-top: 16px; - display: flex; - align-items: center; - justify-content: space-around; - } - img { - max-width: 90%; - max-height: 100%; - } - h2 { - text-align: center; - margin: 16px 8px 0; - } - .attention { - --state-color: var(--error-color); - --text-on-state-color: var(--text-primary-color); - } - .discovered { - --state-color: var(--primary-color); - --text-on-state-color: var(--text-primary-color); - } - .actions { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 6px 0; - height: 48px; - } - `, - ]; - } + static styles = css` + ha-card { + display: flex; + flex-direction: column; + height: 100%; + --ha-card-border-color: var(--state-color); + --mdc-theme-primary: var(--state-color); + } + .filler { + flex: 1; + } + .attention { + --state-color: var(--error-color); + --text-on-state-color: var(--text-primary-color); + } + .discovered { + --state-color: var(--primary-color); + --text-on-state-color: var(--text-primary-color); + } + .actions { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 6px 0; + height: 48px; + } + `; } declare global { diff --git a/src/panels/config/integrations/ha-integration-card.ts b/src/panels/config/integrations/ha-integration-card.ts index 786b394e7e..5aaf0d31c4 100644 --- a/src/panels/config/integrations/ha-integration-card.ts +++ b/src/panels/config/integrations/ha-integration-card.ts @@ -31,7 +31,7 @@ import { } from "../../../data/config_entries"; import type { DeviceRegistryEntry } from "../../../data/device_registry"; import type { EntityRegistryEntry } from "../../../data/entity_registry"; -import { domainToName, IntegrationManifest } from "../../../data/integration"; +import type { IntegrationManifest } from "../../../data/integration"; import { showConfigEntrySystemOptionsDialog } from "../../../dialogs/config-entry-system-options/show-dialog-config-entry-system-options"; import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow"; import { @@ -40,13 +40,9 @@ import { showPromptDialog, } from "../../../dialogs/generic/show-dialog-box"; import { haStyle } from "../../../resources/styles"; -import { HomeAssistant } from "../../../types"; -import { brandsUrl } from "../../../util/brands-url"; -import { ConfigEntryExtended } from "./ha-config-integrations"; -import { - haConfigIntegrationRenderIcons, - haConfigIntegrationsStyles, -} from "./ha-config-integrations-common"; +import type { HomeAssistant } from "../../../types"; +import type { ConfigEntryExtended } from "./ha-config-integrations"; +import "./ha-integration-header"; const ERROR_STATES: ConfigEntry["state"][] = [ "migration_error", @@ -92,18 +88,6 @@ export class HaIntegrationCard extends LitElement { ); } - let primary: string; - let secondary: string | undefined; - - if (item) { - primary = item.title || item.localized_domain_name || this.domain; - if (primary !== item.localized_domain_name) { - secondary = item.localized_domain_name; - } - } else { - primary = domainToName(this.hass.localize, this.domain, this.manifest); - } - const hasItem = item !== undefined; return html` @@ -120,38 +104,32 @@ export class HaIntegrationCard extends LitElement { })}" .configEntry=${item} > - ${this.disabled - ? html` - - ` - : ""} - ${this.items.length > 1 - ? html` -
- -
- ` - : ""} -
- -
-
${primary}
- ${secondary ? html`
${secondary}
` : ""} -
- ${haConfigIntegrationRenderIcons(this.hass, this.manifest)} -
+ + ${this.items.length > 1 + ? html` +
+ +
+ ` + : ""} +
+ ${item ? this._renderSingleEntry(item) : this._renderGroupedIntegration()} @@ -441,14 +419,6 @@ export class HaIntegrationCard extends LitElement { ); } - private _onImageLoad(ev) { - ev.target.style.visibility = "initial"; - } - - private _onImageError(ev) { - ev.target.style.visibility = "hidden"; - } - private _showOptions(ev) { showOptionsFlowDialog(this, ev.target.closest("ha-card").configEntry); } @@ -605,7 +575,6 @@ export class HaIntegrationCard extends LitElement { static get styles(): CSSResult[] { return [ haStyle, - haConfigIntegrationsStyles, css` ha-card { display: flex; @@ -627,7 +596,7 @@ export class HaIntegrationCard extends LitElement { --state-message-color: var(--primary-text-color); } :host(.highlight) ha-card { - --state-color: var(--accent-color); + --state-color: var(--primary-color); --text-on-state-color: var(--text-primary-color); } @@ -645,36 +614,6 @@ export class HaIntegrationCard extends LitElement { height: 0px; } - .header { - display: flex; - position: relative; - align-items: center; - padding: 16px 8px 8px 16px; - } - .header img { - margin-right: 16px; - width: 40px; - height: 40px; - } - .header .info div, - paper-item-body { - word-wrap: break-word; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; - overflow: hidden; - text-overflow: ellipsis; - } - .primary { - font-size: 16px; - font-weight: 400; - color: var(--primary-text-color); - } - .secondary { - font-size: 14px; - color: var(--secondary-text-color); - } - .message { font-weight: bold; padding-bottom: 16px; @@ -724,6 +663,14 @@ export class HaIntegrationCard extends LitElement { cursor: pointer; min-height: 35px; } + paper-item-body { + word-wrap: break-word; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } mwc-list-item ha-svg-icon { color: var(--secondary-text-color); } diff --git a/src/panels/config/integrations/ha-integration-header.ts b/src/panels/config/integrations/ha-integration-header.ts new file mode 100644 index 0000000000..fefebd5f65 --- /dev/null +++ b/src/panels/config/integrations/ha-integration-header.ts @@ -0,0 +1,174 @@ +import { mdiPackageVariant, mdiCloud } from "@mdi/js"; +import "@polymer/paper-tooltip/paper-tooltip"; +import { + css, + html, + customElement, + property, + LitElement, + TemplateResult, +} from "lit-element"; +import { domainToName, IntegrationManifest } from "../../../data/integration"; +import { HomeAssistant } from "../../../types"; +import { brandsUrl } from "../../../util/brands-url"; + +@customElement("ha-integration-header") +export class HaIntegrationHeader extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public banner!: string; + + @property() public localizedDomainName?: string; + + @property() public domain!: string; + + @property() public label!: string; + + @property() public manifest?: IntegrationManifest; + + protected render(): TemplateResult { + let primary: string; + let secondary: string | undefined; + + const domainName = + this.localizedDomainName || + domainToName(this.hass.localize, this.domain, this.manifest); + + if (this.label) { + primary = this.label; + secondary = primary === domainName ? undefined : domainName; + } else { + primary = domainName; + } + + const icons: [string, string][] = []; + + if (this.manifest) { + if (!this.manifest.is_built_in) { + icons.push([ + mdiPackageVariant, + this.hass.localize( + "ui.panel.config.integrations.config_entry.provided_by_custom_integration" + ), + ]); + } + + if ( + this.manifest.iot_class && + this.manifest.iot_class.startsWith("cloud_") + ) { + icons.push([ + mdiCloud, + this.hass.localize( + "ui.panel.config.integrations.config_entry.depends_on_cloud" + ), + ]); + } + } + + return html` + ${!this.banner + ? "" + : html``} + +
+ +
+
${primary}
+ ${secondary ? html`
${secondary}
` : ""} +
+ ${icons.length === 0 + ? "" + : html` +
+ ${icons.map( + ([icon, description]) => html` + + + ${description} + + ` + )} +
+ `} +
+ `; + } + + private _onImageLoad(ev) { + ev.target.style.visibility = "initial"; + } + + private _onImageError(ev) { + ev.target.style.visibility = "hidden"; + } + + static styles = css` + .banner { + background-color: var(--state-color); + color: var(--text-on-state-color); + text-align: center; + padding: 8px; + } + .header { + display: flex; + position: relative; + align-items: center; + padding: 16px 8px 8px 16px; + } + .header img { + margin-right: 16px; + width: 40px; + height: 40px; + } + .header .info div { + word-wrap: break-word; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + } + .primary { + font-size: 16px; + font-weight: 400; + color: var(--primary-text-color); + } + .secondary { + font-size: 14px; + color: var(--secondary-text-color); + } + .icons { + position: absolute; + top: 0px; + right: 16px; + color: var(--text-on-state-color, var(--secondary-text-color)); + background-color: var(--state-color, #e0e0e0); + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + padding: 1px 4px 2px; + } + .icons ha-svg-icon { + width: 20px; + height: 20px; + } + paper-tooltip { + white-space: nowrap; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-integration-header": HaIntegrationHeader; + } +}