Compare commits

...
6 changed files with 206 additions and 98 deletions
+62
View File
@@ -0,0 +1,62 @@
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html } from "lit";
import { customElement, property } from "lit/decorators";
import { HaListItemBase } from "./ha-list-item-base";
/**
* @element ha-list-item-value
* @extends {HaListItemBase}
*
* @summary
* Non-interactive label/value row for grouped lists: label on the start
* side, value content end-aligned. The value is the default slot so callers
* can render rich content (links, secondary lines).
*
* @slot - The value content.
*
* @csspart label - The label column.
* @csspart value - The value column.
*
* @cssprop --ha-list-item-value-max-width - Maximum width of the value column. Defaults to 60%.
*
* @attr {string} label - The row label.
*/
@customElement("ha-list-item-value")
export class HaListItemValue extends HaListItemBase {
@property({ type: String }) public label?: string;
protected override _renderInner(): TemplateResult {
return html`
<div part="label" class="label">${this.label}</div>
<div part="value" class="value"><slot></slot></div>
`;
}
static styles: CSSResultGroup = [
HaListItemBase.styles,
css`
:host {
--ha-row-item-padding-block: var(--ha-space-2);
--ha-row-item-min-height: 40px;
}
.label {
flex: 1;
color: var(--secondary-text-color);
}
.value {
max-width: var(--ha-list-item-value-max-width, 60%);
min-width: 0;
text-align: end;
overflow-wrap: anywhere;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-list-item-value": HaListItemValue;
}
}
+88
View File
@@ -0,0 +1,88 @@
import type { TemplateResult } from "lit";
import { css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { HaListBase } from "./ha-list-base";
/**
* @element ha-grouped-list
* @extends {HaListBase}
*
* @summary
* Grouped list: an optional header above a framed box of rows separated by
* hairlines — the "grouped list" idiom of settings and detail views. Items
* are `<ha-list-item-*>` rows; use `ha-list-item-value` for label/value
* facts and `ha-list-item-button` for navigable rows.
*
* @slot - List items (`<ha-list-item-*>`).
*
* @csspart header - The header above the frame.
* @csspart base - The framed `<div role="list">`.
*
* @cssprop --ha-row-item-padding-inline - Horizontal padding of the rows, which the header aligns to. Defaults to `--ha-space-3`.
*
* @attr {string} header - Header text rendered above the frame.
*/
@customElement("ha-grouped-list")
export class HaGroupedList extends HaListBase {
// The frame carries the list role so the header stays out of the list
// semantics.
protected override readonly hostRole = "";
@property({ type: String }) public header?: string;
protected override render(): TemplateResult {
return html`
${
this.header
? html`<div part="header" class="header" id="header">
${this.header}
</div>`
: nothing
}
<div
part="base"
class="base"
role="list"
aria-labelledby=${ifDefined(this.header ? "header" : undefined)}
>
<slot></slot>
</div>
`;
}
static styles = [
...HaListBase.styles,
css`
:host {
--ha-row-item-padding-inline: var(--ha-space-3);
}
.header {
margin: 0 0 var(--ha-space-1);
margin-inline-start: calc(
var(--ha-row-item-padding-inline) + var(--ha-border-width-sm)
);
font-size: var(--ha-font-size-m);
font-weight: var(--ha-font-weight-medium);
color: var(--secondary-text-color);
}
.base {
border: var(--ha-border-width-sm) solid var(--divider-color);
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
overflow: hidden;
}
::slotted(:not(:first-child)) {
border-top: var(--ha-border-width-sm) solid var(--divider-color);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-grouped-list": HaGroupedList;
}
}
+44 -94
View File
@@ -7,7 +7,8 @@ import { computeAttributeNameDisplay } from "../../common/entity/compute_attribu
import checkValidDate from "../../common/datetime/check_valid_date";
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
import "../../components/ha-attribute-value";
import "../../components/ha-card";
import "../../components/item/ha-list-item-value";
import "../../components/list/ha-grouped-list";
import type { LocalizeKeys } from "../../common/translations/localize";
import { computeShownAttributes } from "../../data/entity/entity_attributes";
import type { ExtEntityRegistryEntry } from "../../data/entity/entity_registry";
@@ -69,43 +70,28 @@ class HaMoreInfoDetails extends LitElement {
in-dialog
></ha-yaml-editor>`
: html`
<section class="section">
<h2 class="section-title">
${this.hass.localize(
"ui.components.entity.entity-state-picker.state"
)}
</h2>
<ha-card>
<div class="card-content">
<div class="data-group">
${stateEntries.map(
(entry) =>
html`<div class="data-entry">
<div class="key">
${this.hass.localize(entry.translationKey)}
</div>
<div class="value">${entry.value}</div>
</div>`
)}
</div>
</div>
</ha-card>
</section>
<ha-grouped-list
.header=${this.hass.localize(
"ui.components.entity.entity-state-picker.state"
)}
>
${stateEntries.map(
(entry) =>
html`<ha-list-item-value
.label=${this.hass.localize(entry.translationKey)}
>
${entry.value}
</ha-list-item-value>`
)}
</ha-grouped-list>
<section class="section">
<h2 class="section-title">
${this.hass.localize(
"ui.dialogs.more_info_control.attributes"
)}
</h2>
<ha-card>
<div class="card-content">
<div class="data-group">
${this._renderAttributes(attributes)}
</div>
</div>
</ha-card>
</section>
<ha-grouped-list
.header=${this.hass.localize(
"ui.dialogs.more_info_control.attributes"
)}
>
${this._renderAttributes(attributes)}
</ha-grouped-list>
`
}
</div>
@@ -192,28 +178,25 @@ class HaMoreInfoDetails extends LitElement {
return attributes.map(
(attribute) => html`
<div class="data-entry">
<div class="key">
${computeAttributeNameDisplay(
this.hass.localize,
this._stateObj!,
this.hass.entities,
attribute
)}
</div>
<div class="value">
${
attribute === "supported_features" && featureEnum
? this._renderFeatures(featureEnum, this._stateObj!)
: html`
<ha-attribute-value
.attribute=${attribute}
.stateObj=${this._stateObj}
></ha-attribute-value>
`
}
</div>
</div>
<ha-list-item-value
.label=${computeAttributeNameDisplay(
this.hass.localize,
this._stateObj!,
this.hass.entities,
attribute
)}
>
${
attribute === "supported_features" && featureEnum
? this._renderFeatures(featureEnum, this._stateObj!)
: html`
<ha-attribute-value
.attribute=${attribute}
.stateObj=${this._stateObj}
></ha-attribute-value>
`
}
</ha-list-item-value>
`
);
}
@@ -247,47 +230,14 @@ class HaMoreInfoDetails extends LitElement {
padding-bottom: max(var(--safe-area-inset-bottom), var(--ha-space-6));
}
.section + .section {
ha-grouped-list + ha-grouped-list {
margin-top: var(--ha-space-4);
}
.section-title {
margin: 0 0 var(--ha-space-2);
font-size: var(--ha-font-size-m);
font-weight: var(--ha-font-weight-medium);
}
.card-content {
padding: var(--ha-space-2) var(--ha-space-4);
}
.data-entry {
display: flex;
flex-direction: row;
justify-content: space-between;
padding: var(--ha-space-2) 0;
border-bottom: 1px solid var(--divider-color);
}
.data-group .data-entry:last-of-type {
border-bottom: none;
}
.data-entry .value {
max-width: 60%;
overflow-wrap: break-word;
text-align: right;
}
.key {
flex-grow: 1;
color: var(--secondary-text-color);
}
.empty {
color: var(--secondary-text-color);
text-align: center;
padding: var(--ha-space-2) 0;
padding: var(--ha-space-3) var(--ha-space-4);
}
`;
}
@@ -92,7 +92,7 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow {
</div>
`
: html`
<div class="flex state">
<div class="flex box">
<ha-input
.disabled=${stateObj.state === UNAVAILABLE}
pattern="[0-9]+([\\.][0-9]+)?"
@@ -128,6 +128,10 @@ class HuiInputNumberEntityRow extends LitElement implements LovelaceRow {
min-width: 45px;
text-align: end;
}
.box {
flex-grow: 0;
min-width: 45px;
}
ha-input {
width: 100%;
}
@@ -99,7 +99,7 @@ class HuiNumberEntityRow extends LitElement implements LovelaceRow {
</div>
`
: html`
<div class="flex state">
<div class="flex box">
<ha-input
auto-validate
.disabled=${stateObj.state === UNAVAILABLE}
@@ -136,6 +136,10 @@ class HuiNumberEntityRow extends LitElement implements LovelaceRow {
min-width: 45px;
text-align: end;
}
.box {
flex-grow: 0;
min-width: 45px;
}
ha-input::part(wa-input) {
text-align: end;
direction: ltr !important;
+2 -2
View File
@@ -77,8 +77,8 @@ export const moreInfoViewElements: ViewElementSmokeCase<MoreInfoView>[] = [
{
view: "details",
element: "ha-more-info-details",
// The details view renders the state and attributes cards.
content: [{ selector: "ha-card" }],
// The details view renders the state and attributes grouped lists.
content: [{ selector: "ha-grouped-list" }],
},
];