Tile card editor improvements (#24373)

* Add selector support

* Feedbacks

* Use select box fields in tile card editor
This commit is contained in:
Paul Bottein 2025-02-24 15:26:55 +01:00 committed by GitHub
parent 2b1f301db6
commit 0cfe7f8d12
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 119 additions and 38 deletions

View File

@ -0,0 +1,7 @@
<svg width="94" height="40" viewBox="0 0 94 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="40" rx="8" fill="white"/>
<rect x="0.5" y="0.5" width="93" height="39" rx="7.5" stroke="black" stroke-opacity="0.12"/>
<circle cx="20" cy="20" r="12" fill="black" fill-opacity="0.12"/>
<path d="M40 14C40 10.6863 42.6863 8 46 8H65C68.3137 8 71 10.6863 71 14C71 17.3137 68.3137 20 65 20H46C42.6863 20 40 17.3137 40 14Z" fill="black" fill-opacity="0.32"/>
<path d="M40 28C40 25.7909 41.7909 24 44 24H77C79.2091 24 81 25.7909 81 28C81 30.2091 79.2091 32 77 32H44C41.7909 32 40 30.2091 40 28Z" fill="black" fill-opacity="0.32"/>
</svg>

After

Width:  |  Height:  |  Size: 652 B

View File

@ -0,0 +1,7 @@
<svg width="94" height="40" viewBox="0 0 94 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="40" rx="8" fill="black"/>
<rect x="0.5" y="0.5" width="93" height="39" rx="7.5" stroke="white" stroke-opacity="0.24"/>
<circle cx="20" cy="20" r="12" fill="white" fill-opacity="0.24"/>
<path d="M40 14C40 10.6863 42.6863 8 46 8H65C68.3137 8 71 10.6863 71 14C71 17.3137 68.3137 20 65 20H46C42.6863 20 40 17.3137 40 14Z" fill="white" fill-opacity="0.48"/>
<path d="M40 28C40 25.7909 41.7909 24 44 24H77C79.2091 24 81 25.7909 81 28C81 30.2091 79.2091 32 77 32H44C41.7909 32 40 30.2091 40 28Z" fill="white" fill-opacity="0.48"/>
</svg>

After

Width:  |  Height:  |  Size: 652 B

View File

@ -0,0 +1,7 @@
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="72" rx="8" fill="white"/>
<rect x="0.5" y="0.5" width="93" height="71" rx="7.5" stroke="black" stroke-opacity="0.12"/>
<circle cx="47" cy="20" r="12" fill="black" fill-opacity="0.12"/>
<path d="M31.5 46C31.5 42.6863 34.1863 40 37.5 40H56.5C59.8137 40 62.5 42.6863 62.5 46C62.5 49.3137 59.8137 52 56.5 52H37.5C34.1863 52 31.5 49.3137 31.5 46Z" fill="black" fill-opacity="0.32"/>
<path d="M26.5 60C26.5 57.7909 28.2909 56 30.5 56H63.5C65.7091 56 67.5 57.7909 67.5 60C67.5 62.2091 65.7091 64 63.5 64H30.5C28.2909 64 26.5 62.2091 26.5 60Z" fill="black" fill-opacity="0.32"/>
</svg>

After

Width:  |  Height:  |  Size: 699 B

View File

@ -0,0 +1,7 @@
<svg width="94" height="72" viewBox="0 0 94 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="72" rx="8" fill="black"/>
<rect x="0.5" y="0.5" width="93" height="71" rx="7.5" stroke="white" stroke-opacity="0.24"/>
<circle cx="47" cy="20" r="12" fill="white" fill-opacity="0.24"/>
<path d="M31.5 46C31.5 42.6863 34.1863 40 37.5 40H56.5C59.8137 40 62.5 42.6863 62.5 46C62.5 49.3137 59.8137 52 56.5 52H37.5C34.1863 52 31.5 49.3137 31.5 46Z" fill="white" fill-opacity="0.48"/>
<path d="M26.5 60C26.5 57.7909 28.2909 56 30.5 56H63.5C65.7091 56 67.5 57.7909 67.5 60C67.5 62.2091 65.7091 64 63.5 64H30.5C28.2909 64 26.5 62.2091 26.5 60Z" fill="white" fill-opacity="0.48"/>
</svg>

After

Width:  |  Height:  |  Size: 699 B

View File

@ -0,0 +1,6 @@
<svg width="94" height="48" viewBox="0 0 94 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="48" rx="8" fill="white"/>
<rect x="0.5" y="0.5" width="93" height="47" rx="7.5" stroke="black" stroke-opacity="0.12"/>
<rect x="8" y="8" width="78" height="12" rx="3" fill="black" fill-opacity="0.12"/>
<rect x="8" y="28" width="78" height="12" rx="3" fill="black" fill-opacity="0.32"/>
</svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@ -0,0 +1,6 @@
<svg width="94" height="48" viewBox="0 0 94 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="48" rx="8" fill="black"/>
<rect x="0.5" y="0.5" width="93" height="47" rx="7.5" stroke="white" stroke-opacity="0.24"/>
<rect x="8" y="8" width="78" height="12" rx="3" fill="white" fill-opacity="0.24"/>
<rect x="8" y="28" width="78" height="12" rx="3" fill="white" fill-opacity="0.48"/>
</svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@ -0,0 +1,6 @@
<svg width="94" height="28" viewBox="0 0 94 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="28" rx="8" fill="white"/>
<rect x="0.5" y="0.5" width="93" height="27" rx="7.5" stroke="black" stroke-opacity="0.12"/>
<rect x="8" y="8" width="35" height="12" rx="3" fill="black" fill-opacity="0.12"/>
<rect x="51" y="8" width="35" height="12" rx="3" fill="black" fill-opacity="0.32"/>
</svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@ -0,0 +1,6 @@
<svg width="94" height="28" viewBox="0 0 94 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="94" height="28" rx="8" fill="black"/>
<rect x="0.5" y="0.5" width="93" height="27" rx="7.5" stroke="white" stroke-opacity="0.24"/>
<rect x="8" y="8" width="35" height="12" rx="3" fill="white" fill-opacity="0.24"/>
<rect x="51" y="8" width="35" height="12" rx="3" fill="white" fill-opacity="0.48"/>
</svg>

After

Width:  |  Height:  |  Size: 414 B

View File

@ -115,7 +115,7 @@ export class HuiTileCardEditor
localize: LocalizeFunc, localize: LocalizeFunc,
entityId: string | undefined, entityId: string | undefined,
hideState: boolean, hideState: boolean,
vertical: boolean, isDark: boolean,
displayActions: AdvancedActions[] = [] displayActions: AdvancedActions[] = []
) => ) =>
[ [
@ -175,41 +175,20 @@ export class HuiTileCardEditor
] as const satisfies readonly HaFormSchema[]) ] as const satisfies readonly HaFormSchema[])
: []), : []),
{ {
name: "", name: "content_layout",
type: "grid", required: true,
schema: [ selector: {
{ select: {
name: "content_layout", mode: "box",
required: true, options: ["horizontal", "vertical"].map((value) => ({
selector: { label: localize(
select: { `ui.panel.lovelace.editor.card.tile.content_layout_options.${value}`
mode: "dropdown", ),
options: ["horizontal", "vertical"].map((value) => ({ value,
label: localize( image: `/static/images/form/tile_content_layout_${value}${isDark ? "_dark" : ""}.svg`,
`ui.panel.lovelace.editor.card.tile.content_layout_options.${value}` })),
),
value,
})),
},
},
}, },
{ },
name: "features_position",
required: true,
selector: {
select: {
mode: "dropdown",
options: ["bottom", "inline"].map((value) => ({
label: localize(
`ui.panel.lovelace.editor.card.tile.features_position_options.${value}`
),
value,
disabled: vertical && value === "inline",
})),
},
},
},
],
}, },
], ],
}, },
@ -250,6 +229,29 @@ export class HuiTileCardEditor
] as const satisfies readonly HaFormSchema[] ] as const satisfies readonly HaFormSchema[]
); );
private _featuresSchema = memoizeOne(
(localize: LocalizeFunc, vertical: boolean, isDark: boolean) =>
[
{
name: "features_position",
required: true,
selector: {
select: {
mode: "box",
options: ["bottom", "inline"].map((value) => ({
label: localize(
`ui.panel.lovelace.editor.card.tile.features_position_options.${value}`
),
value,
image: `/static/images/form/tile_features_position_${value}${isDark ? "_dark" : ""}.svg`,
disabled: vertical && value === "inline",
})),
},
},
},
] as const satisfies readonly HaFormSchema[]
);
protected render() { protected render() {
if (!this.hass || !this._config) { if (!this.hass || !this._config) {
return nothing; return nothing;
@ -262,10 +264,16 @@ export class HuiTileCardEditor
this.hass.localize, this.hass.localize,
entityId, entityId,
this._config.hide_state ?? false, this._config.hide_state ?? false,
this._config.vertical ?? false, this.hass.themes.darkMode,
this._displayActions this._displayActions
); );
const featuresSchema = this._featuresSchema(
this.hass.localize,
this._config.vertical ?? false,
this.hass.themes.darkMode
);
const data = { const data = {
...this._config, ...this._config,
content_layout: this._config.vertical ? "vertical" : "horizontal", content_layout: this._config.vertical ? "vertical" : "horizontal",
@ -293,6 +301,15 @@ export class HuiTileCardEditor
)} )}
</h3> </h3>
<div class="content"> <div class="content">
<ha-form
class="features-form"
.hass=${this.hass}
.data=${data}
.schema=${featuresSchema}
.computeLabel=${this._computeLabelCallback}
.computeHelper=${this._computeHelperCallback}
@value-changed=${this._valueChanged}
></ha-form>
<hui-card-features-editor <hui-card-features-editor
.hass=${this.hass} .hass=${this.hass}
.stateObj=${stateObj} .stateObj=${stateObj}
@ -381,7 +398,9 @@ export class HuiTileCardEditor
} }
private _computeLabelCallback = ( private _computeLabelCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>> schema:
| SchemaUnion<ReturnType<typeof this._schema>>
| SchemaUnion<ReturnType<typeof this._featuresSchema>>
) => { ) => {
switch (schema.name) { switch (schema.name) {
case "color": case "color":
@ -405,13 +424,22 @@ export class HuiTileCardEditor
}; };
private _computeHelperCallback = ( private _computeHelperCallback = (
schema: SchemaUnion<ReturnType<typeof this._schema>> schema:
| SchemaUnion<ReturnType<typeof this._schema>>
| SchemaUnion<ReturnType<typeof this._featuresSchema>>
) => { ) => {
switch (schema.name) { switch (schema.name) {
case "color": case "color":
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.tile.${schema.name}_helper` `ui.panel.lovelace.editor.card.tile.${schema.name}_helper`
); );
case "features_position":
if (this._config?.vertical) {
return this.hass!.localize(
`ui.panel.lovelace.editor.card.tile.${schema.name}_helper_vertical`
);
}
return undefined;
default: default:
return undefined; return undefined;
} }

View File

@ -7158,6 +7158,7 @@
"bottom": "Bottom", "bottom": "Bottom",
"inline": "Inline" "inline": "Inline"
}, },
"features_position_helper_vertical": "Always displayed at the bottom if the content layout is vertical",
"content_layout": "Content layout", "content_layout": "Content layout",
"content_layout_options": { "content_layout_options": {
"horizontal": "Horizontal", "horizontal": "Horizontal",