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:
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user