Compare commits

..

4 Commits

Author SHA1 Message Date
renovate[bot]
43a23e6cdd Update dependency marked to v16.4.0 (#27436)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-10 17:55:08 +02:00
renovate[bot]
aa4dd1cf29 Update dependency @codemirror/view to v6.38.5 (#27445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-10 17:48:25 +02:00
Paul Bottein
0ae55c39cc Use ha-icon-button for media player more info (#27449)
Use ha-icon-buttom for media player more info
2025-10-10 17:19:48 +02:00
Aidan Timson
0bfacacc9e Update hide sections header helper translation (#27421) 2025-10-10 17:12:38 +02:00
8 changed files with 43 additions and 409 deletions

View File

@@ -34,7 +34,7 @@
"@codemirror/legacy-modes": "6.5.2",
"@codemirror/search": "6.5.11",
"@codemirror/state": "6.5.2",
"@codemirror/view": "6.38.4",
"@codemirror/view": "6.38.5",
"@date-fns/tz": "1.4.1",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.18.1",
@@ -122,7 +122,7 @@
"lit": "3.3.1",
"lit-html": "3.3.1",
"luxon": "3.7.2",
"marked": "16.3.0",
"marked": "16.4.0",
"memoize-one": "6.0.0",
"node-vibrant": "4.0.3",
"object-hash": "3.0.0",

View File

@@ -167,15 +167,12 @@ class MoreInfoMediaPlayer extends LitElement {
}
return html`<ha-md-button-menu positioning="popover">
<ha-button
<ha-icon-button
slot="trigger"
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(`ui.card.media_player.source`)}
.title=${this.hass.localize(`ui.card.media_player.source`)}
.path=${mdiLoginVariant}
>
<ha-svg-icon .path=${mdiLoginVariant}></ha-svg-icon>
</ha-button>
</ha-icon-button>
${this.stateObj.attributes.source_list!.map(
(source) =>
html`<ha-md-menu-item
@@ -203,15 +200,12 @@ class MoreInfoMediaPlayer extends LitElement {
}
return html`<ha-md-button-menu positioning="popover">
<ha-button
<ha-icon-button
slot="trigger"
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(`ui.card.media_player.sound_mode`)}
.title=${this.hass.localize(`ui.card.media_player.sound_mode`)}
.path=${mdiMusicNoteEighth}
>
<ha-svg-icon .path=${mdiMusicNoteEighth}></ha-svg-icon>
</ha-button>
</ha-icon-button>
${this.stateObj.attributes.sound_mode_list!.map(
(soundMode) =>
html`<ha-md-menu-item
@@ -237,21 +231,17 @@ class MoreInfoMediaPlayer extends LitElement {
const groupMembers = this.stateObj.attributes.group_members;
const hasMultipleMembers = groupMembers && groupMembers?.length > 1;
return html`<ha-button
class="grouping"
return html`<ha-icon-button
@click=${this._showGroupMediaPlayers}
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize("ui.card.media_player.join")}
.title=${this.hass.localize("ui.card.media_player.join")}
>
<div>
<div class="grouping">
<ha-svg-icon .path=${mdiSpeakerMultiple}></ha-svg-icon>
${hasMultipleMembers
? html`<span class="badge"> ${groupMembers?.length || 4} </span>`
? html`<span class="badge">${groupMembers?.length || 4}</span>`
: nothing}
</div>
</ha-button>`;
</ha-icon-button>`;
}
protected _renderEmptyCover(title: string, icon?: string) {
@@ -414,48 +404,39 @@ class MoreInfoMediaPlayer extends LitElement {
${!isUnavailableState(stateObj.state) &&
supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA)
? html`
<ha-button
<ha-icon-button
@click=${this._showBrowseMedia}
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(
.title=${this.hass.localize(
"ui.card.media_player.browse_media"
)}
.path=${mdiPlayBoxMultiple}
>
<ha-svg-icon .path=${mdiPlayBoxMultiple}></ha-svg-icon>
</ha-button>
</ha-icon-button>
`
: nothing}
${this._renderGrouping()} ${this._renderSourceControl()}
${this._renderSoundMode()}
${turnOn
? html`<ha-button
? html`<ha-icon-button
action=${turnOn.action}
@click=${this._handleClick}
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(
.title=${this.hass.localize(
`ui.card.media_player.${turnOn.action}`
)}
.path=${turnOn.icon}
>
<ha-svg-icon .path=${turnOn.icon}></ha-svg-icon>
</ha-button>`
</ha-icon-button>`
: nothing}
${turnOff
? html`<ha-button
? html`<ha-icon-button
action=${turnOff.action}
@click=${this._handleClick}
appearance="plain"
variant="neutral"
size="small"
title=${this.hass.localize(
.title=${this.hass.localize(
`ui.card.media_player.${turnOff.action}`
)}
.path=${turnOff.icon}
>
<ha-svg-icon .path=${turnOff.icon}></ha-svg-icon>
</ha-button>`
</ha-icon-button>`
: nothing}
</div>
</div>
@@ -579,7 +560,7 @@ class MoreInfoMediaPlayer extends LitElement {
font-size: var(--ha-font-size-xs);
background-color: var(--primary-color);
padding: 0 4px;
color: var(--primary-text-color);
color: var(--text-primary-color);
}
.position-bar {
@@ -616,15 +597,15 @@ class MoreInfoMediaPlayer extends LitElement {
justify-content: space-around;
}
.controls-row ha-button {
width: 32px;
.controls-row ha-icon-button {
color: var(--secondary-text-color);
}
.controls-row ha-svg-icon {
color: var(--ha-color-on-neutral-quiet);
}
.grouping::part(label) {
.grouping {
position: relative;
}

View File

@@ -1,5 +1,4 @@
import "../heading-badges/hui-entity-heading-badge";
import "../heading-badges/hui-button-heading-badge";
import {
createLovelaceElement,
@@ -7,7 +6,7 @@ import {
} from "./create-element-base";
import type { LovelaceHeadingBadgeConfig } from "../heading-badges/types";
const ALWAYS_LOADED_TYPES = new Set(["error", "entity", "button"]);
const ALWAYS_LOADED_TYPES = new Set(["error", "entity"]);
export const createHeadingBadgeElement = (config: LovelaceHeadingBadgeConfig) =>
createLovelaceElement(

View File

@@ -1,238 +0,0 @@
import { mdiEye, mdiGestureTap, mdiTextShort } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { any, array, assert, object, optional, string } from "superstruct";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import type { HomeAssistant } from "../../../../types";
import type { Condition } from "../../common/validate-condition";
import type { UiAction } from "../../components/hui-action-editor";
import type { ButtonHeadingBadgeConfig } from "../../heading-badges/types";
import type { LovelaceGenericElementEditor } from "../../types";
import "../conditions/ha-card-conditions-editor";
import { configElementStyle } from "../config-elements/config-elements-style";
import { actionConfigStruct } from "../structs/action-struct";
export const DEFAULT_CONFIG: Partial<ButtonHeadingBadgeConfig> = {
type: "button",
};
const entityConfigStruct = object({
type: optional(string()),
text: optional(string()),
icon: optional(string()),
tap_action: optional(actionConfigStruct),
hold_action: optional(actionConfigStruct),
double_tap_action: optional(actionConfigStruct),
visibility: optional(array(any())),
});
const ALLOWED_ACTIONS: UiAction[] = [
"navigate",
"url",
"assist",
"call-service",
"none",
];
@customElement("hui-heading-button-editor")
export class HuiHeadingButtonEditor
extends LitElement
implements LovelaceGenericElementEditor
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public preview = false;
@state() private _config?: ButtonHeadingBadgeConfig;
public setConfig(config: ButtonHeadingBadgeConfig): void {
assert(config, entityConfigStruct);
this._config = {
...DEFAULT_CONFIG,
...config,
};
}
private _schema = memoizeOne(
() =>
[
{
name: "content",
type: "expandable",
flatten: true,
iconPath: mdiTextShort,
schema: [
{
name: "",
type: "grid",
schema: [
{
name: "text",
selector: {
text: {},
},
},
{
name: "icon",
selector: { icon: {} },
context: { icon_entity: "entity" },
},
],
},
],
},
{
name: "interactions",
type: "expandable",
flatten: true,
iconPath: mdiGestureTap,
schema: [
{
name: "tap_action",
selector: {
ui_action: {
default_action: "none",
actions: ALLOWED_ACTIONS,
},
},
},
{
name: "",
type: "optional_actions",
flatten: true,
schema: (["hold_action", "double_tap_action"] as const).map(
(action) => ({
name: action,
selector: {
ui_action: {
default_action: "none" as const,
actions: ALLOWED_ACTIONS,
},
},
})
),
},
],
},
] as const satisfies readonly HaFormSchema[]
);
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
const schema = this._schema();
const conditions = this._config.visibility ?? [];
return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${schema}
.computeLabel=${this._computeLabelCallback}
@value-changed=${this._valueChanged}
></ha-form>
<ha-expansion-panel outlined>
<ha-svg-icon slot="leading-icon" .path=${mdiEye}></ha-svg-icon>
<h3 slot="header">
${this.hass!.localize(
"ui.panel.lovelace.editor.card.heading.entity_config.visibility"
)}
</h3>
<div class="content">
<p class="intro">
${this.hass.localize(
"ui.panel.lovelace.editor.card.heading.entity_config.visibility_explanation"
)}
</p>
<ha-card-conditions-editor
.hass=${this.hass}
.conditions=${conditions}
@value-changed=${this._conditionChanged}
>
</ha-card-conditions-editor>
</div>
</ha-expansion-panel>
`;
}
private _valueChanged(ev: CustomEvent): void {
ev.stopPropagation();
if (!this._config || !this.hass) {
return;
}
const config = { ...ev.detail.value } as FormData;
fireEvent(this, "config-changed", { config });
}
private _conditionChanged(ev: CustomEvent): void {
ev.stopPropagation();
if (!this._config || !this.hass) {
return;
}
const conditions = ev.detail.value as Condition[];
const newConfig: ButtonHeadingBadgeConfig = {
...this._config,
visibility: conditions,
};
if (newConfig.visibility?.length === 0) {
delete newConfig.visibility;
}
fireEvent(this, "config-changed", { config: newConfig });
}
private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
switch (schema.name) {
case "text":
return this.hass!.localize(
`ui.panel.lovelace.editor.card.heading.button_config.${schema.name}`
);
default:
return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}`
);
}
};
static get styles() {
return [
configElementStyle,
css`
.container {
display: flex;
flex-direction: column;
}
ha-form {
display: block;
margin-bottom: 24px;
}
.intro {
margin: 0;
color: var(--secondary-text-color);
margin-bottom: 8px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-heading-button-editor": HuiHeadingButtonEditor;
}
}

View File

@@ -1,96 +0,0 @@
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../components/ha-state-icon";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import "../../../state-display/state-display";
import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action";
import { DEFAULT_CONFIG } from "../editor/heading-badge-editor/hui-entity-heading-badge-editor";
import type {
LovelaceHeadingBadge,
LovelaceHeadingBadgeEditor,
} from "../types";
import type { ButtonHeadingBadgeConfig } from "./types";
const DEFAULT_ACTIONS: Pick<
ButtonHeadingBadgeConfig,
"tap_action" | "hold_action" | "double_tap_action"
> = {
tap_action: { action: "none" },
hold_action: { action: "none" },
double_tap_action: { action: "none" },
};
@customElement("hui-button-heading-badge")
export class HuiButtonHeadingBadge
extends LitElement
implements LovelaceHeadingBadge
{
public static async getConfigElement(): Promise<LovelaceHeadingBadgeEditor> {
await import(
"../editor/heading-badge-editor/hui-button-heading-badge-editor"
);
return document.createElement("hui-heading-button-editor");
}
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _config?: ButtonHeadingBadgeConfig;
@property({ type: Boolean }) public preview = false;
public setConfig(config): void {
this._config = {
...DEFAULT_CONFIG,
...DEFAULT_ACTIONS,
...config,
};
}
get hasAction() {
return (
hasAction(this._config?.tap_action) ||
hasAction(this._config?.hold_action) ||
hasAction(this._config?.double_tap_action)
);
}
private _handleAction(ev: ActionHandlerEvent) {
handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
protected render() {
if (!this.hass || !this._config) {
return nothing;
}
const config = this._config;
return html`
<ha-button
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config!.hold_action),
hasDoubleClick: hasAction(this._config!.double_tap_action),
})}
>
<ha-icon .icon=${config.icon}></ha-icon>
${this._config.text}
</ha-button>
`;
}
static styles = css`
[role="button"] {
cursor: pointer;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"hui-button-heading-badge": HuiButtonHeadingBadge;
}
}

View File

@@ -26,12 +26,3 @@ export interface EntityHeadingBadgeConfig extends LovelaceHeadingBadgeConfig {
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}
export interface ButtonHeadingBadgeConfig extends LovelaceHeadingBadgeConfig {
type?: "button";
icon: string;
text?: string;
tap_action: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}

View File

@@ -7766,9 +7766,6 @@
"state": "[%key:ui::panel::lovelace::editor::badge::entity::displayed_elements_options::state%]"
}
},
"button_config": {
"text": "Text"
},
"default_heading": "Kitchen"
},
"map": {
@@ -7939,7 +7936,7 @@
"hide_completed": "Hide completed items",
"hide_create": "Hide 'Add item' field",
"hide_section_headers": "Hide section headers",
"hide_section_headers_helper": "Removes the 'Active' and 'Completed' sections with the overflow menus.",
"hide_section_headers_helper": "Removes the 'Active' and 'Completed' section headers and their overflow menus.",
"display_order": "Display order",
"item_tap_action": "Item tap behavior",
"actions": {

View File

@@ -1284,15 +1284,15 @@ __metadata:
languageName: node
linkType: hard
"@codemirror/view@npm:6.38.4, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0":
version: 6.38.4
resolution: "@codemirror/view@npm:6.38.4"
"@codemirror/view@npm:6.38.5, @codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0":
version: 6.38.5
resolution: "@codemirror/view@npm:6.38.5"
dependencies:
"@codemirror/state": "npm:^6.5.0"
crelt: "npm:^1.0.6"
style-mod: "npm:^4.1.0"
w3c-keyname: "npm:^2.2.4"
checksum: 10/86b3894e9e7c2113aabb1db8684d0520378339c194fa56a688fc26cd7d40336bb9df1f5f19f68309d95f14b80ecf0b70c0ffe5e43f2ec11c4bab18f2d5ee4494
checksum: 10/2335b593770042eb3adfe369073432b07cd2d15f1e230ae4dc7be7a7b8edd74e57c13e59b92a11e7e5d59ae030aabf7f55478dfec1cf2a2fe3a1ef3f091676a4
languageName: node
linkType: hard
@@ -9189,7 +9189,7 @@ __metadata:
"@codemirror/legacy-modes": "npm:6.5.2"
"@codemirror/search": "npm:6.5.11"
"@codemirror/state": "npm:6.5.2"
"@codemirror/view": "npm:6.38.4"
"@codemirror/view": "npm:6.38.5"
"@date-fns/tz": "npm:1.4.1"
"@egjs/hammerjs": "npm:2.0.17"
"@formatjs/intl-datetimeformat": "npm:6.18.1"
@@ -9332,7 +9332,7 @@ __metadata:
lodash.template: "npm:4.5.0"
luxon: "npm:3.7.2"
map-stream: "npm:0.0.7"
marked: "npm:16.3.0"
marked: "npm:16.4.0"
memoize-one: "npm:6.0.0"
node-vibrant: "npm:4.0.3"
object-hash: "npm:3.0.0"
@@ -10974,12 +10974,12 @@ __metadata:
languageName: node
linkType: hard
"marked@npm:16.3.0":
version: 16.3.0
resolution: "marked@npm:16.3.0"
"marked@npm:16.4.0":
version: 16.4.0
resolution: "marked@npm:16.4.0"
bin:
marked: bin/marked.js
checksum: 10/60497834b9acfb3b3994222509d359ecb9a197c885dfeb77e2050a287cd2f4ab19f00d5597172b47f9e0c54d9e1e13d8b2dd73322b7838599e1f16d1d6283f5b
checksum: 10/5174b345ccc61e2030c2eb8abb3e5cbebeb6697a6d2b609f64ffa2ff6e482f5f1e1fda1912db19c747f43971b1fa54ae53c1ab1ce5d2f58566d6db4bc3016833
languageName: node
linkType: hard