1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 00:27:49 +01:00

Allow boolean option to section background (#30289)

This commit is contained in:
Paul Bottein
2026-03-23 18:32:53 +01:00
committed by GitHub
parent c462fc0639
commit 02acd2996c
5 changed files with 55 additions and 35 deletions

View File

@@ -14,7 +14,7 @@ export interface LovelaceBaseSectionConfig {
disabled?: boolean;
column_span?: number;
row_span?: number;
background?: LovelaceSectionBackgroundConfig;
background?: boolean | LovelaceSectionBackgroundConfig;
/**
* @deprecated Use heading card instead.
*/
@@ -34,6 +34,15 @@ export type LovelaceSectionRawConfig =
| LovelaceSectionConfig
| LovelaceStrategySectionConfig;
export function resolveSectionBackground(
background: boolean | LovelaceSectionBackgroundConfig | undefined
): LovelaceSectionBackgroundConfig | undefined {
if (typeof background === "boolean") {
return background ? {} : undefined;
}
return background;
}
export function isStrategySection(
section: LovelaceSectionRawConfig
): section is LovelaceStrategySectionConfig {

View File

@@ -3,17 +3,18 @@ import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import type {
HaFormSchema,
SchemaUnion,
} from "../../../../components/ha-form/types";
import "../../../../components/ha-form/ha-form";
import {
DEFAULT_SECTION_BACKGROUND_OPACITY,
resolveSectionBackground,
type LovelaceSectionRawConfig,
} from "../../../../data/lovelace/config/section";
import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
import type { LocalizeFunc } from "../../../../common/translations/localize";
import type { HomeAssistant } from "../../../../types";
interface SettingsData {
@@ -95,13 +96,14 @@ export class HuiDialogEditSection extends LitElement {
render() {
const backgroundEnabled = this.config.background !== undefined;
const background = resolveSectionBackground(this.config.background);
const data: SettingsData = {
column_span: this.config.column_span || 1,
background_enabled: backgroundEnabled,
background_color: this.config.background?.color ?? "default",
background_color: background?.color ?? "default",
background_opacity:
this.config.background?.opacity ?? DEFAULT_SECTION_BACKGROUND_OPACITY,
background?.opacity ?? DEFAULT_SECTION_BACKGROUND_OPACITY,
};
const schema = this._schema(
@@ -146,12 +148,13 @@ export class HuiDialogEditSection extends LitElement {
};
if (newData.background_enabled) {
const hasCustomColor =
newData.background_color !== undefined &&
newData.background_color !== "default";
newConfig.background = {
...(newData.background_color && newData.background_color !== "default"
? { color: newData.background_color }
: {}),
opacity:
newData.background_opacity ?? DEFAULT_SECTION_BACKGROUND_OPACITY,
...(hasCustomColor ? { color: newData.background_color } : {}),
opacity: newData.background_opacity!,
};
} else {
delete newConfig.background;

View File

@@ -1,16 +1,17 @@
import { css, LitElement, nothing } from "lit";
import { css, LitElement, nothing, unsafeCSS } from "lit";
import type { PropertyValues } from "lit";
import { customElement, property } from "lit/decorators";
import { computeCssColor } from "../../../common/color/compute-color";
import {
DEFAULT_SECTION_BACKGROUND_OPACITY,
resolveSectionBackground,
type LovelaceSectionBackgroundConfig,
} from "../../../data/lovelace/config/section";
@customElement("hui-section-background")
export class HuiSectionBackground extends LitElement {
@property({ attribute: false })
public background?: LovelaceSectionBackgroundConfig;
public background?: boolean | LovelaceSectionBackgroundConfig;
protected render() {
return nothing;
@@ -18,14 +19,15 @@ export class HuiSectionBackground extends LitElement {
protected willUpdate(changedProperties: PropertyValues<this>) {
super.willUpdate(changedProperties);
if (changedProperties.has("background") && this.background) {
const color = this.background.color
? computeCssColor(this.background.color)
: "var(--ha-section-background-color, var(--secondary-background-color))";
this.style.setProperty("--section-background", color);
const opacity =
this.background.opacity ?? DEFAULT_SECTION_BACKGROUND_OPACITY;
this.style.setProperty("--section-background-opacity", `${opacity}%`);
if (changedProperties.has("background")) {
const resolved = resolveSectionBackground(this.background);
if (resolved) {
const color = resolved.color ? computeCssColor(resolved.color) : null;
this.style.setProperty("--section-background-color", color);
const opacity =
resolved.opacity !== undefined ? `${resolved.opacity}%` : null;
this.style.setProperty("--section-background-opacity", opacity);
}
}
}
@@ -34,8 +36,14 @@ export class HuiSectionBackground extends LitElement {
position: absolute;
inset: 0;
border-radius: inherit;
background-color: var(--section-background, none);
opacity: var(--section-background-opacity, 100%);
background-color: var(
--section-background-color,
var(--ha-section-background-color, var(--secondary-background-color))
);
opacity: var(
--section-background-opacity,
${unsafeCSS(DEFAULT_SECTION_BACKGROUND_OPACITY)}%
);
z-index: 0;
pointer-events: none;
}

View File

@@ -30,11 +30,11 @@ import {
import type { HuiSection } from "../sections/hui-section";
import "../sections/hui-section-background";
import type { Lovelace } from "../types";
import { computeSectionsBackgroundAlignment } from "./sections-background-alignment";
import { generateDefaultSection } from "./default-section";
import "./hui-view-footer";
import "./hui-view-header";
import "./hui-view-sidebar";
import { computeSectionsBackgroundAlignment } from "./sections-background-alignment";
export const DEFAULT_MAX_COLUMNS = 4;

View File

@@ -4,7 +4,7 @@ import { computeSectionsBackgroundAlignment } from "../../../../src/panels/lovel
function mockSection(
opts: {
background?: {};
background?: boolean;
column_span?: number;
hidden?: boolean;
} = {}
@@ -20,7 +20,7 @@ function mockSection(
describe("computeSectionsBackgroundAlignment", () => {
it("returns empty set for single column layout", () => {
const sections = [mockSection(), mockSection({ background: {} })];
const sections = [mockSection(), mockSection({ background: true })];
const result = computeSectionsBackgroundAlignment(sections, 1);
expect(result.size).toBe(0);
});
@@ -33,15 +33,15 @@ describe("computeSectionsBackgroundAlignment", () => {
it("returns empty set when all sections have background", () => {
const sections = [
mockSection({ background: {} }),
mockSection({ background: {} }),
mockSection({ background: true }),
mockSection({ background: true }),
];
const result = computeSectionsBackgroundAlignment(sections, 2);
expect(result.size).toBe(0);
});
it("marks section without background on same row as one with background", () => {
const sections = [mockSection(), mockSection({ background: {} })];
const sections = [mockSection(), mockSection({ background: true })];
const result = computeSectionsBackgroundAlignment(sections, 2);
expect(result.has(0)).toBe(true);
expect(result.has(1)).toBe(false);
@@ -49,7 +49,7 @@ describe("computeSectionsBackgroundAlignment", () => {
it("does not mark sections on different rows", () => {
// Row 1: section 0 (no bg), Row 2: section 1 (bg)
const sections = [mockSection(), mockSection({ background: {} })];
const sections = [mockSection(), mockSection({ background: true })];
const result = computeSectionsBackgroundAlignment(sections, 1);
expect(result.size).toBe(0);
});
@@ -59,7 +59,7 @@ describe("computeSectionsBackgroundAlignment", () => {
// section 2 (span 1, no bg) = row 2
const sections = [
mockSection({ column_span: 2 }),
mockSection({ column_span: 2, background: {} }),
mockSection({ column_span: 2, background: true }),
mockSection(),
];
const result = computeSectionsBackgroundAlignment(sections, 4);
@@ -71,7 +71,7 @@ describe("computeSectionsBackgroundAlignment", () => {
it("wraps to new row when column_span exceeds remaining space", () => {
// 2 columns: section 0 (span 1, bg) on row 1, section 1 (span 2, no bg) on row 2
const sections = [
mockSection({ background: {} }),
mockSection({ background: true }),
mockSection({ column_span: 2 }),
];
const result = computeSectionsBackgroundAlignment(sections, 2);
@@ -81,7 +81,7 @@ describe("computeSectionsBackgroundAlignment", () => {
it("skips hidden sections", () => {
// section 0 (hidden, bg) should not cause section 1 to need margin
const sections = [
mockSection({ hidden: true, background: {} }),
mockSection({ hidden: true, background: true }),
mockSection(),
];
const result = computeSectionsBackgroundAlignment(sections, 2);
@@ -94,7 +94,7 @@ describe("computeSectionsBackgroundAlignment", () => {
// Row 2: section 2 (no bg) + section 3 (no bg) -> no margin needed
const sections = [
mockSection(),
mockSection({ background: {} }),
mockSection({ background: true }),
mockSection(),
mockSection(),
];
@@ -114,7 +114,7 @@ describe("computeSectionsBackgroundAlignment", () => {
// section with span 10 in a 2-column layout should be treated as span 2
// Row 1: section 0 (span clamped to 2, bg), Row 2: section 1 (no bg)
const sections = [
mockSection({ column_span: 10, background: {} }),
mockSection({ column_span: 10, background: true }),
mockSection(),
];
const result = computeSectionsBackgroundAlignment(sections, 2);
@@ -125,7 +125,7 @@ describe("computeSectionsBackgroundAlignment", () => {
// 3 columns: section 0 (no bg) + section 1 (bg) + section 2 (no bg)
const sections = [
mockSection(),
mockSection({ background: {} }),
mockSection({ background: true }),
mockSection(),
];
const result = computeSectionsBackgroundAlignment(sections, 3);