mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 11:16:35 +00:00
Add inline features position for tile card (#24199)
* Add side features position for tile card * Add translations * Rename to inline * Simplify editor with 2 dropdowns * Use 50% width * Update src/translations/en.json Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com> --------- Co-authored-by: Wendelin <12148533+wendevlin@users.noreply.github.com>
This commit is contained in:
parent
94a5e737cc
commit
0b64861297
@ -20,7 +20,6 @@ export class HuiCardFeatures extends LitElement {
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<div class="container">
|
|
||||||
${this.features.map(
|
${this.features.map(
|
||||||
(feature) => html`
|
(feature) => html`
|
||||||
<hui-card-feature
|
<hui-card-feature
|
||||||
@ -31,29 +30,21 @@ export class HuiCardFeatures extends LitElement {
|
|||||||
></hui-card-feature>
|
></hui-card-feature>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
:host {
|
:host {
|
||||||
--feature-color: var(--state-icon-color);
|
--feature-color: var(--state-icon-color);
|
||||||
--feature-padding: 12px;
|
|
||||||
--feature-height: 42px;
|
--feature-height: 42px;
|
||||||
--feature-border-radius: 12px;
|
--feature-border-radius: 12px;
|
||||||
--feature-button-spacing: 12px;
|
--feature-button-spacing: 12px;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: var(--feature-padding);
|
gap: 12px;
|
||||||
padding-top: 0px;
|
|
||||||
gap: var(--feature-padding);
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
}
|
}
|
||||||
|
@ -256,6 +256,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
|
|||||||
hui-card-features {
|
hui-card-features {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: none;
|
flex: none;
|
||||||
|
padding: 0 12px 12px 12px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -248,6 +248,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
hui-card-features {
|
hui-card-features {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex: none;
|
flex: none;
|
||||||
|
padding: 0 12px 12px 12px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -100,10 +100,13 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getCardSize(): number {
|
public getCardSize(): number {
|
||||||
|
const featuresPosition =
|
||||||
|
this._config && this._featurePosition(this._config);
|
||||||
|
const featuresCount = this._config?.features?.length || 0;
|
||||||
return (
|
return (
|
||||||
1 +
|
1 +
|
||||||
(this._config?.vertical ? 1 : 0) +
|
(this._config?.vertical ? 1 : 0) +
|
||||||
(this._config?.features?.length || 0)
|
(featuresPosition === "inline" ? 0 : featuresCount)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,9 +114,16 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
const columns = 6;
|
const columns = 6;
|
||||||
let min_columns = 6;
|
let min_columns = 6;
|
||||||
let rows = 1;
|
let rows = 1;
|
||||||
if (this._config?.features?.length) {
|
const featurePosition = this._config && this._featurePosition(this._config);
|
||||||
rows += this._config.features.length;
|
const featuresCount = this._config?.features?.length || 0;
|
||||||
|
if (featuresCount) {
|
||||||
|
if (featurePosition === "inline") {
|
||||||
|
min_columns = 12;
|
||||||
|
} else {
|
||||||
|
rows += featuresCount;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this._config?.vertical) {
|
if (this._config?.vertical) {
|
||||||
rows++;
|
rows++;
|
||||||
min_columns = 3;
|
min_columns = 3;
|
||||||
@ -210,6 +220,23 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _featurePosition = memoizeOne((config: TileCardConfig) => {
|
||||||
|
if (config.vertical) {
|
||||||
|
return "bottom";
|
||||||
|
}
|
||||||
|
return config.features_position || "bottom";
|
||||||
|
});
|
||||||
|
|
||||||
|
private _displayedFeatures = memoizeOne((config: TileCardConfig) => {
|
||||||
|
const features = config.features || [];
|
||||||
|
const featurePosition = this._featurePosition(config);
|
||||||
|
|
||||||
|
if (featurePosition === "inline") {
|
||||||
|
return features.slice(0, 1);
|
||||||
|
}
|
||||||
|
return features;
|
||||||
|
});
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this._config || !this.hass) {
|
if (!this._config || !this.hass) {
|
||||||
return nothing;
|
return nothing;
|
||||||
@ -263,6 +290,12 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
? this._getImageUrl(stateObj)
|
? this._getImageUrl(stateObj)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const featurePosition = this._featurePosition(this._config);
|
||||||
|
const features = this._displayedFeatures(this._config);
|
||||||
|
|
||||||
|
const containerOrientationClass =
|
||||||
|
featurePosition === "inline" ? "horizontal" : "";
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card style=${styleMap(style)} class=${classMap({ active })}>
|
<ha-card style=${styleMap(style)} class=${classMap({ active })}>
|
||||||
<div
|
<div
|
||||||
@ -278,7 +311,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
>
|
>
|
||||||
<ha-ripple .disabled=${!this._hasCardAction}></ha-ripple>
|
<ha-ripple .disabled=${!this._hasCardAction}></ha-ripple>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container ${containerOrientationClass}">
|
||||||
<div class="content ${classMap(contentClasses)}">
|
<div class="content ${classMap(contentClasses)}">
|
||||||
<ha-tile-icon
|
<ha-tile-icon
|
||||||
role=${ifDefined(this._hasIconAction ? "button" : undefined)}
|
role=${ifDefined(this._hasIconAction ? "button" : undefined)}
|
||||||
@ -308,13 +341,13 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
.secondary=${stateDisplay}
|
.secondary=${stateDisplay}
|
||||||
></ha-tile-info>
|
></ha-tile-info>
|
||||||
</div>
|
</div>
|
||||||
${this._config.features
|
${features.length > 0
|
||||||
? html`
|
? html`
|
||||||
<hui-card-features
|
<hui-card-features
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.color=${this._config.color}
|
.color=${this._config.color}
|
||||||
.features=${this._config.features}
|
.features=${features}
|
||||||
></hui-card-features>
|
></hui-card-features>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
@ -372,6 +405,10 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
.container.horizontal {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -383,6 +420,11 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.container.horizontal .content {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
.vertical {
|
.vertical {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -413,6 +455,13 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
hui-card-features {
|
hui-card-features {
|
||||||
--feature-color: var(--tile-color);
|
--feature-color: var(--tile-color);
|
||||||
|
padding: 0 12px 12px 12px;
|
||||||
|
}
|
||||||
|
.container.horizontal hui-card-features {
|
||||||
|
width: 50%;
|
||||||
|
--feature-height: 36px;
|
||||||
|
padding: 10px;
|
||||||
|
padding-inline-start: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-tile-icon[data-domain="alarm_control_panel"][data-state="pending"],
|
ha-tile-icon[data-domain="alarm_control_panel"][data-state="pending"],
|
||||||
|
@ -533,6 +533,7 @@ export interface TileCardConfig extends LovelaceCardConfig {
|
|||||||
icon_hold_action?: ActionConfig;
|
icon_hold_action?: ActionConfig;
|
||||||
icon_double_tap_action?: ActionConfig;
|
icon_double_tap_action?: ActionConfig;
|
||||||
features?: LovelaceCardFeatureConfig[];
|
features?: LovelaceCardFeatureConfig[];
|
||||||
|
features_position?: "bottom" | "inline";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HeadingCardConfig extends LovelaceCardConfig {
|
export interface HeadingCardConfig extends LovelaceCardConfig {
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
assert,
|
assert,
|
||||||
assign,
|
assign,
|
||||||
boolean,
|
boolean,
|
||||||
|
enums,
|
||||||
object,
|
object,
|
||||||
optional,
|
optional,
|
||||||
string,
|
string,
|
||||||
@ -15,6 +16,7 @@ import {
|
|||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
|
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||||
import "../../../../components/ha-expansion-panel";
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type {
|
import type {
|
||||||
@ -54,6 +56,7 @@ const cardConfigStruct = assign(
|
|||||||
icon_hold_action: optional(actionConfigStruct),
|
icon_hold_action: optional(actionConfigStruct),
|
||||||
icon_double_tap_action: optional(actionConfigStruct),
|
icon_double_tap_action: optional(actionConfigStruct),
|
||||||
features: optional(array(any())),
|
features: optional(array(any())),
|
||||||
|
features_position: optional(enums(["bottom", "inline"])),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -109,8 +112,10 @@ export class HuiTileCardEditor
|
|||||||
|
|
||||||
private _schema = memoizeOne(
|
private _schema = memoizeOne(
|
||||||
(
|
(
|
||||||
|
localize: LocalizeFunc,
|
||||||
entityId: string | undefined,
|
entityId: string | undefined,
|
||||||
hideState: boolean,
|
hideState: boolean,
|
||||||
|
vertical: boolean,
|
||||||
displayActions: AdvancedActions[] = []
|
displayActions: AdvancedActions[] = []
|
||||||
) =>
|
) =>
|
||||||
[
|
[
|
||||||
@ -148,12 +153,6 @@ export class HuiTileCardEditor
|
|||||||
boolean: {},
|
boolean: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "vertical",
|
|
||||||
selector: {
|
|
||||||
boolean: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "hide_state",
|
name: "hide_state",
|
||||||
selector: {
|
selector: {
|
||||||
@ -175,6 +174,43 @@ export class HuiTileCardEditor
|
|||||||
},
|
},
|
||||||
] as const satisfies readonly HaFormSchema[])
|
] as const satisfies readonly HaFormSchema[])
|
||||||
: []),
|
: []),
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
type: "grid",
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: "content_layout",
|
||||||
|
required: true,
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
mode: "dropdown",
|
||||||
|
options: ["horizontal", "vertical"].map((value) => ({
|
||||||
|
label: localize(
|
||||||
|
`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",
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -223,12 +259,22 @@ export class HuiTileCardEditor
|
|||||||
const stateObj = entityId ? this.hass!.states[entityId] : undefined;
|
const stateObj = entityId ? this.hass!.states[entityId] : undefined;
|
||||||
|
|
||||||
const schema = this._schema(
|
const schema = this._schema(
|
||||||
|
this.hass.localize,
|
||||||
entityId,
|
entityId,
|
||||||
this._config!.hide_state ?? false,
|
this._config.hide_state ?? false,
|
||||||
|
this._config.vertical ?? false,
|
||||||
this._displayActions
|
this._displayActions
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = this._config;
|
const data = {
|
||||||
|
...this._config,
|
||||||
|
content_layout: this._config.vertical ? "vertical" : "horizontal",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default features position to bottom and force it to bottom in vertical mode
|
||||||
|
if (!data.features_position || data.vertical) {
|
||||||
|
data.features_position = "bottom";
|
||||||
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-form
|
<ha-form
|
||||||
@ -280,6 +326,12 @@ export class HuiTileCardEditor
|
|||||||
delete config.state_content;
|
delete config.state_content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert content_layout to vertical
|
||||||
|
if (config.content_layout) {
|
||||||
|
config.vertical = config.content_layout === "vertical";
|
||||||
|
delete config.content_layout;
|
||||||
|
}
|
||||||
|
|
||||||
fireEvent(this, "config-changed", { config });
|
fireEvent(this, "config-changed", { config });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,11 +389,11 @@ export class HuiTileCardEditor
|
|||||||
case "icon_hold_action":
|
case "icon_hold_action":
|
||||||
case "icon_double_tap_action":
|
case "icon_double_tap_action":
|
||||||
case "show_entity_picture":
|
case "show_entity_picture":
|
||||||
case "vertical":
|
|
||||||
case "hide_state":
|
case "hide_state":
|
||||||
case "state_content":
|
case "state_content":
|
||||||
|
case "content_layout":
|
||||||
case "appearance":
|
case "appearance":
|
||||||
case "interactions":
|
case "features_position":
|
||||||
return this.hass!.localize(
|
return this.hass!.localize(
|
||||||
`ui.panel.lovelace.editor.card.tile.${schema.name}`
|
`ui.panel.lovelace.editor.card.tile.${schema.name}`
|
||||||
);
|
);
|
||||||
@ -377,6 +429,14 @@ export class HuiTileCardEditor
|
|||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
.info {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.features-form {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -7119,12 +7119,20 @@
|
|||||||
"icon_tap_action": "Icon tap behavior",
|
"icon_tap_action": "Icon tap behavior",
|
||||||
"icon_hold_action": "Icon hold behavior",
|
"icon_hold_action": "Icon hold behavior",
|
||||||
"icon_double_tap_action": "Icon double tap behavior",
|
"icon_double_tap_action": "Icon double tap behavior",
|
||||||
"interactions": "Interactions",
|
|
||||||
"appearance": "Appearance",
|
"appearance": "Appearance",
|
||||||
"show_entity_picture": "Show entity picture",
|
"show_entity_picture": "Show entity picture",
|
||||||
"vertical": "Vertical",
|
|
||||||
"hide_state": "Hide state",
|
"hide_state": "Hide state",
|
||||||
"state_content": "State content"
|
"state_content": "State content",
|
||||||
|
"features_position": "Features position",
|
||||||
|
"features_position_options": {
|
||||||
|
"bottom": "Bottom",
|
||||||
|
"inline": "Inline"
|
||||||
|
},
|
||||||
|
"content_layout": "Content layout",
|
||||||
|
"content_layout_options": {
|
||||||
|
"horizontal": "Horizontal",
|
||||||
|
"vertical": "Vertical"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"vertical-stack": {
|
"vertical-stack": {
|
||||||
"name": "Vertical stack",
|
"name": "Vertical stack",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user