Add leading icon slot to expansion panel and fix left-chevron property (#24635)

* Add leading icon slot to expansion panel and fix left chevron

* Update components
This commit is contained in:
Paul Bottein 2025-03-17 20:23:20 +01:00 committed by GitHub
parent 4f7d5053ec
commit 3c11323ea4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 105 additions and 86 deletions

View File

@ -1,4 +1,4 @@
import { mdiPacMan } from "@mdi/js"; import { mdiLightbulbOn, mdiPacMan } from "@mdi/js";
import type { TemplateResult } from "lit"; import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
@ -125,6 +125,23 @@ const SAMPLES: {
`; `;
}, },
}, },
{
template(slot, leftChevron) {
return html`
<ha-expansion-panel
slot=${slot}
.leftChevron=${leftChevron}
header="Attr Header with actions"
>
<ha-svg-icon
slot="leading-icon"
.path=${mdiLightbulbOn}
></ha-svg-icon>
${SHORT_TEXT}
</ha-expansion-panel>
`;
},
},
]; ];
@customElement("demo-components-ha-expansion-panel") @customElement("demo-components-ha-expansion-panel")

View File

@ -1,6 +1,6 @@
import { mdiChevronDown } from "@mdi/js"; import { mdiChevronDown } from "@mdi/js";
import type { PropertyValues, TemplateResult } from "lit"; import type { PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
@ -13,11 +13,11 @@ export class HaExpansionPanel extends LitElement {
@property({ type: Boolean, reflect: true }) outlined = false; @property({ type: Boolean, reflect: true }) outlined = false;
@property({ attribute: false, type: Boolean, reflect: true }) leftChevron = @property({ attribute: "left-chevron", type: Boolean, reflect: true })
false; public leftChevron = false;
@property({ attribute: false, type: Boolean, reflect: true }) noCollapse = @property({ attribute: "no-collapse", type: Boolean, reflect: true })
false; public noCollapse = false;
@property() header?: string; @property() header?: string;
@ -28,6 +28,14 @@ export class HaExpansionPanel extends LitElement {
@query(".container") private _container!: HTMLDivElement; @query(".container") private _container!: HTMLDivElement;
protected render(): TemplateResult { protected render(): TemplateResult {
const chevronIcon = this.noCollapse
? nothing
: html`
<ha-svg-icon
.path=${mdiChevronDown}
class="summary-icon ${classMap({ expanded: this.expanded })}"
></ha-svg-icon>
`;
return html` return html`
<div class="top ${classMap({ expanded: this.expanded })}"> <div class="top ${classMap({ expanded: this.expanded })}">
<div <div
@ -42,28 +50,15 @@ export class HaExpansionPanel extends LitElement {
aria-expanded=${this.expanded} aria-expanded=${this.expanded}
aria-controls="sect1" aria-controls="sect1"
> >
${this.leftChevron && !this.noCollapse ${this.leftChevron ? chevronIcon : nothing}
? html` <slot name="leading-icon"></slot>
<ha-svg-icon
.path=${mdiChevronDown}
class="summary-icon ${classMap({ expanded: this.expanded })}"
></ha-svg-icon>
`
: ""}
<slot name="header"> <slot name="header">
<div class="header"> <div class="header">
${this.header} ${this.header}
<slot class="secondary" name="secondary">${this.secondary}</slot> <slot class="secondary" name="secondary">${this.secondary}</slot>
</div> </div>
</slot> </slot>
${!this.leftChevron && !this.noCollapse ${!this.leftChevron ? chevronIcon : nothing}
? html`
<ha-svg-icon
.path=${mdiChevronDown}
class="summary-icon ${classMap({ expanded: this.expanded })}"
></ha-svg-icon>
`
: ""}
<slot name="icons"></slot> <slot name="icons"></slot>
</div> </div>
</div> </div>
@ -177,7 +172,8 @@ export class HaExpansionPanel extends LitElement {
margin-inline-end: initial; margin-inline-end: initial;
} }
:host([leftchevron]) .summary-icon { :host([left-chevron]) .summary-icon,
::slotted([slot="leading-icon"]) {
margin-left: 0; margin-left: 0;
margin-right: 8px; margin-right: 8px;
margin-inline-start: 0; margin-inline-start: 0;

View File

@ -45,7 +45,7 @@ export class HaFilterBlueprints extends LitElement {
protected render() { protected render() {
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -65,7 +65,7 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) {
protected render() { protected render() {
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -46,7 +46,7 @@ export class HaFilterDevices extends LitElement {
protected render() { protected render() {
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -33,7 +33,7 @@ export class HaFilterDomains extends LitElement {
protected render() { protected render() {
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -48,7 +48,7 @@ export class HaFilterEntities extends LitElement {
protected render() { protected render() {
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -54,7 +54,7 @@ export class HaFilterFloorAreas extends LitElement {
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -35,7 +35,7 @@ export class HaFilterIntegrations extends LitElement {
protected render() { protected render() {
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -71,7 +71,7 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) {
protected render() { protected render() {
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -41,7 +41,7 @@ export class HaFilterStates extends LitElement {
const hasIcon = this.states.find((item) => item.icon); const hasIcon = this.states.find((item) => item.icon);
return html` return html`
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
.expanded=${this.expanded} .expanded=${this.expanded}
@expanded-will-change=${this._expandedWillChange} @expanded-will-change=${this._expandedWillChange}
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}

View File

@ -67,18 +67,23 @@ export class HaFormExpendable extends LitElement implements HaFormElement {
protected render() { protected render() {
return html` return html`
<ha-expansion-panel outlined .expanded=${Boolean(this.schema.expanded)}> <ha-expansion-panel outlined .expanded=${Boolean(this.schema.expanded)}>
${this.schema.icon
? html`
<ha-icon slot="leading-icon" .icon=${this.schema.icon}></ha-icon>
`
: this.schema.iconPath
? html`
<ha-svg-icon
slot="leading-icon"
.path=${this.schema.iconPath}
></ha-svg-icon>
`
: nothing}
<div <div
slot="header" slot="header"
role="heading" role="heading"
aria-level=${this.schema.headingLevel?.toString() ?? "3"} aria-level=${this.schema.headingLevel?.toString() ?? "3"}
> >
${this.schema.icon
? html` <ha-icon .icon=${this.schema.icon}></ha-icon> `
: this.schema.iconPath
? html`
<ha-svg-icon .path=${this.schema.iconPath}></ha-svg-icon>
`
: nothing}
${this.schema.title || this.computeLabel?.(this.schema)} ${this.schema.title || this.computeLabel?.(this.schema)}
</div> </div>
<div class="content"> <div class="content">

View File

@ -523,7 +523,7 @@ export class HaServiceControl extends LitElement {
return fields.length && return fields.length &&
this._hasFilteredFields(fields, targetEntities) this._hasFilteredFields(fields, targetEntities)
? html`<ha-expansion-panel ? html`<ha-expansion-panel
leftChevron left-chevron
.expanded=${!dataField.collapsed} .expanded=${!dataField.collapsed}
.header=${this.hass.localize( .header=${this.hass.localize(
`component.${domain}.services.${serviceName}.sections.${dataField.key}.name` `component.${domain}.services.${serviceName}.sections.${dataField.key}.name`

View File

@ -203,20 +203,24 @@ export default class HaAutomationActionRow extends LitElement {
</div> </div>
` `
: nothing} : nothing}
<ha-expansion-panel leftChevron> <ha-expansion-panel left-chevron>
<h3 slot="header"> ${type === "service" && "action" in this.action && this.action.action
${type === "service" && ? html`
"action" in this.action && <ha-service-icon
this.action.action slot="leading-icon"
? html`<ha-service-icon
class="action-icon" class="action-icon"
.hass=${this.hass} .hass=${this.hass}
.service=${this.action.action} .service=${this.action.action}
></ha-service-icon>` ></ha-service-icon>
: html`<ha-svg-icon `
: html`
<ha-svg-icon
slot="leading-icon"
class="action-icon" class="action-icon"
.path=${ACTION_ICONS[type!]} .path=${ACTION_ICONS[type!]}
></ha-svg-icon>`} ></ha-svg-icon>
`}
<h3 slot="header">
${capitalizeFirstLetter( ${capitalizeFirstLetter(
describeAction( describeAction(
this.hass, this.hass,
@ -640,9 +644,6 @@ export default class HaAutomationActionRow extends LitElement {
display: inline-block; display: inline-block;
color: var(--secondary-text-color); color: var(--secondary-text-color);
opacity: 0.9; opacity: 0.9;
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
} }
} }
.card-content { .card-content {

View File

@ -128,12 +128,13 @@ export default class HaAutomationConditionRow extends LitElement {
` `
: ""} : ""}
<ha-expansion-panel leftChevron> <ha-expansion-panel left-chevron>
<ha-svg-icon
slot="leading-icon"
class="condition-icon"
.path=${CONDITION_ICONS[this.condition.condition]}
></ha-svg-icon>
<h3 slot="header"> <h3 slot="header">
<ha-svg-icon
class="condition-icon"
.path=${CONDITION_ICONS[this.condition.condition]}
></ha-svg-icon>
${capitalizeFirstLetter( ${capitalizeFirstLetter(
describeCondition(this.condition, this.hass, this._entityReg) describeCondition(this.condition, this.hass, this._entityReg)
)} )}
@ -526,9 +527,6 @@ export default class HaAutomationConditionRow extends LitElement {
display: inline-block; display: inline-block;
color: var(--secondary-text-color); color: var(--secondary-text-color);
opacity: 0.9; opacity: 0.9;
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
} }
} }
.card-content { .card-content {

View File

@ -92,7 +92,7 @@ export default class HaAutomationOptionRow extends LitElement {
return html` return html`
<ha-card outlined> <ha-card outlined>
<ha-expansion-panel <ha-expansion-panel
leftChevron left-chevron
@expanded-changed=${this._expandedChanged} @expanded-changed=${this._expandedChanged}
id="option" id="option"
> >

View File

@ -159,12 +159,13 @@ export default class HaAutomationTriggerRow extends LitElement {
` `
: nothing} : nothing}
<ha-expansion-panel leftChevron> <ha-expansion-panel left-chevron>
<ha-svg-icon
slot="leading-icon"
class="trigger-icon"
.path=${TRIGGER_ICONS[type]}
></ha-svg-icon>
<h3 slot="header"> <h3 slot="header">
<ha-svg-icon
class="trigger-icon"
.path=${TRIGGER_ICONS[type]}
></ha-svg-icon>
${describeTrigger(this.trigger, this.hass, this._entityReg)} ${describeTrigger(this.trigger, this.hass, this._entityReg)}
</h3> </h3>
@ -672,9 +673,6 @@ export default class HaAutomationTriggerRow extends LitElement {
display: inline-block; display: inline-block;
color: var(--secondary-text-color); color: var(--secondary-text-color);
opacity: 0.9; opacity: 0.9;
margin-right: 8px;
margin-inline-end: 8px;
margin-inline-start: initial;
} }
} }
.card-content { .card-content {

View File

@ -130,13 +130,16 @@ export abstract class HaBlueprintGenericEditor extends LitElement {
.expanded=${expanded} .expanded=${expanded}
.noCollapse=${anyRequired} .noCollapse=${anyRequired}
> >
<div slot="header" role="heading" aria-level="3" class="section-header"> ${section?.icon
${section?.icon ? html`
? html`<ha-icon <ha-icon
slot="leading-icon"
class="section-header" class="section-header"
.icon=${section.icon} .icon=${section.icon}
></ha-icon>` ></ha-icon>
: nothing} `
: nothing}
<div slot="header" role="heading" aria-level="3" class="section-header">
<ha-markdown .content=${title}></ha-markdown> <ha-markdown .content=${title}></ha-markdown>
</div> </div>
<div class="content"> <div class="content">

View File

@ -82,7 +82,7 @@ export default class HaScriptFieldRow extends LitElement {
return html` return html`
<ha-card outlined> <ha-card outlined>
<ha-expansion-panel leftChevron> <ha-expansion-panel left-chevron>
<h3 slot="header">${this.key}</h3> <h3 slot="header">${this.key}</h3>
<slot name="icons" slot="icons"></slot> <slot name="icons" slot="icons"></slot>

View File

@ -97,12 +97,13 @@ export class HaCardConditionEditor extends LitElement {
return html` return html`
<div class="container"> <div class="container">
<ha-expansion-panel leftChevron> <ha-expansion-panel left-chevron>
<ha-svg-icon
slot="leading-icon"
class="condition-icon"
.path=${ICON_CONDITION[condition.condition]}
></ha-svg-icon>
<h3 slot="header"> <h3 slot="header">
<ha-svg-icon
class="condition-icon"
.path=${ICON_CONDITION[condition.condition]}
></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
`ui.panel.lovelace.editor.condition-editor.condition.${condition.condition}.label` `ui.panel.lovelace.editor.condition-editor.condition.${condition.condition}.label`
) || condition.condition} ) || condition.condition}

View File

@ -43,7 +43,7 @@ export const configElementStyle = css`
ha-expansion-panel .content { ha-expansion-panel .content {
padding: 12px; padding: 12px;
} }
ha-expansion-panel > * { ha-expansion-panel > *[slot="header"] {
margin: 0; margin: 0;
font-size: inherit; font-size: inherit;
font-weight: inherit; font-weight: inherit;

View File

@ -137,8 +137,8 @@ export class HuiHeadingCardEditor
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-form> ></ha-form>
<ha-expansion-panel outlined> <ha-expansion-panel outlined>
<ha-svg-icon slot="leading-icon" .path=${mdiListBox}></ha-svg-icon>
<h3 slot="header"> <h3 slot="header">
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.card.heading.entities" "ui.panel.lovelace.editor.card.heading.entities"
)} )}

View File

@ -102,8 +102,8 @@ export class HuiHumidifierCardEditor
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-form> ></ha-form>
<ha-expansion-panel outlined> <ha-expansion-panel outlined>
<ha-svg-icon slot="leading-icon" .path=${mdiListBox}></ha-svg-icon>
<h3 slot="header"> <h3 slot="header">
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.features" "ui.panel.lovelace.editor.card.generic.features"
)} )}

View File

@ -101,8 +101,8 @@ export class HuiThermostatCardEditor
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-form> ></ha-form>
<ha-expansion-panel outlined> <ha-expansion-panel outlined>
<ha-svg-icon slot="leading-icon" .path=${mdiListBox}></ha-svg-icon>
<h3 slot="header"> <h3 slot="header">
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.features" "ui.panel.lovelace.editor.card.generic.features"
)} )}

View File

@ -310,8 +310,8 @@ export class HuiTileCardEditor
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-form> ></ha-form>
<ha-expansion-panel outlined> <ha-expansion-panel outlined>
<ha-svg-icon slot="leading-icon" .path=${mdiListBox}></ha-svg-icon>
<h3 slot="header"> <h3 slot="header">
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.card.generic.features" "ui.panel.lovelace.editor.card.generic.features"
)} )}

View File

@ -191,8 +191,8 @@ export class HuiHeadingEntityEditor
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-form> ></ha-form>
<ha-expansion-panel outlined> <ha-expansion-panel outlined>
<ha-svg-icon slot="leading-icon" .path=${mdiEye}></ha-svg-icon>
<h3 slot="header"> <h3 slot="header">
<ha-svg-icon .path=${mdiEye}></ha-svg-icon>
${this.hass!.localize( ${this.hass!.localize(
"ui.panel.lovelace.editor.card.heading.entity_config.visibility" "ui.panel.lovelace.editor.card.heading.entity_config.visibility"
)} )}