Skip to content

Commit ed7a9d9

Browse files
committed
Reduce duplication
1 parent 4192378 commit ed7a9d9

File tree

6 files changed

+64
-143
lines changed

6 files changed

+64
-143
lines changed

src/mixins/conditional-listener-mixin.ts

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import type { ReactiveElement } from "lit";
2+
import type { HomeAssistant } from "../types";
3+
import {
4+
setupMediaQueryListeners,
5+
setupTimeListeners,
6+
} from "../common/condition/listeners";
27

38
type Constructor<T> = abstract new (...args: any[]) => T;
49

@@ -10,13 +15,14 @@ type Constructor<T> = abstract new (...args: any[]) => T;
1015
*
1116
* Usage:
1217
* 1. Extend your component with ConditionalListenerMixin(ReactiveElement)
13-
* 2. Override setupConditionalListeners() to setup your listeners
14-
* 3. Use addConditionalListener() to register unsubscribe functions
15-
* 4. Call clearConditionalListeners() and setupConditionalListeners() when config changes
18+
* 2. Ensure component has config.visibility or _config.visibility property with conditions
19+
* 3. Ensure component has _updateVisibility() or _updateElement() method
20+
* 4. Override setupConditionalListeners() if custom behavior needed (e.g., filter conditions)
1621
*
1722
* The mixin automatically:
1823
* - Sets up listeners when component connects to DOM
1924
* - Cleans up listeners when component disconnects from DOM
25+
* - Handles both media query and time-based conditional visibility
2026
*/
2127
export const ConditionalListenerMixin = <
2228
T extends Constructor<ReactiveElement>,
@@ -26,6 +32,9 @@ export const ConditionalListenerMixin = <
2632
abstract class ConditionalListenerClass extends superClass {
2733
private __listeners: (() => void)[] = [];
2834

35+
// Type hint for hass property (should be provided by subclass)
36+
abstract hass?: HomeAssistant;
37+
2938
public connectedCallback() {
3039
super.connectedCallback();
3140
this.setupConditionalListeners();
@@ -45,8 +54,52 @@ export const ConditionalListenerMixin = <
4554
this.__listeners.push(unsubscribe);
4655
}
4756

48-
protected setupConditionalListeners(): void {
49-
// Override in subclass
57+
/**
58+
* Setup conditional listeners for visibility control
59+
*
60+
* Default implementation:
61+
* - Checks config.visibility or _config.visibility for conditions (if not provided)
62+
* - Sets up media query listeners
63+
* - Sets up time-based listeners
64+
* - Calls _updateVisibility() or _updateElement() when conditions change
65+
*
66+
* Override this method to customize behavior (e.g., filter conditions first)
67+
* and call super.setupConditionalListeners(customConditions) to reuse the base implementation
68+
*
69+
* @param conditions - Optional conditions array. If not provided, will check config.visibility or _config.visibility
70+
*/
71+
protected setupConditionalListeners(conditions?: any[]): void {
72+
const component = this as any;
73+
const finalConditions =
74+
conditions ||
75+
component.config?.visibility ||
76+
component._config?.visibility;
77+
78+
if (!finalConditions || !this.hass) {
79+
return;
80+
}
81+
82+
const onUpdate = (conditionsMet: boolean) => {
83+
if (component._updateVisibility) {
84+
component._updateVisibility(conditionsMet);
85+
} else if (component._updateElement) {
86+
component._updateElement(conditionsMet);
87+
}
88+
};
89+
90+
setupMediaQueryListeners(
91+
finalConditions,
92+
this.hass,
93+
(unsub) => this.addConditionalListener(unsub),
94+
onUpdate
95+
);
96+
97+
setupTimeListeners(
98+
finalConditions,
99+
this.hass,
100+
(unsub) => this.addConditionalListener(unsub),
101+
onUpdate
102+
);
50103
}
51104
}
52105
return ConditionalListenerClass;

src/panels/lovelace/badges/hui-badge.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ import "../../../components/ha-svg-icon";
66
import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
77
import type { HomeAssistant } from "../../../types";
88
import { ConditionalListenerMixin } from "../../../mixins/conditional-listener-mixin";
9-
import {
10-
setupMediaQueryListeners,
11-
setupTimeListeners,
12-
} from "../../../common/condition/listeners";
139
import { checkConditionsMet } from "../common/validate-condition";
1410
import { createBadgeElement } from "../create-element/create-badge-element";
1511
import { createErrorBadgeConfig } from "../create-element/create-element-base";
@@ -134,30 +130,6 @@ export class HuiBadge extends ConditionalListenerMixin(ReactiveElement) {
134130
}
135131
}
136132

137-
protected setupConditionalListeners() {
138-
if (!this.config?.visibility || !this.hass) {
139-
return;
140-
}
141-
142-
setupMediaQueryListeners(
143-
this.config.visibility,
144-
this.hass,
145-
(unsub) => this.addConditionalListener(unsub),
146-
(conditionsMet) => {
147-
this._updateVisibility(conditionsMet);
148-
}
149-
);
150-
151-
setupTimeListeners(
152-
this.config.visibility,
153-
this.hass,
154-
(unsub) => this.addConditionalListener(unsub),
155-
(conditionsMet) => {
156-
this._updateVisibility(conditionsMet);
157-
}
158-
);
159-
}
160-
161133
private _updateVisibility(ignoreConditions?: boolean) {
162134
if (!this._element || !this.hass) {
163135
return;

src/panels/lovelace/cards/hui-card.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@ import "../../../components/ha-svg-icon";
66
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
77
import type { HomeAssistant } from "../../../types";
88
import { ConditionalListenerMixin } from "../../../mixins/conditional-listener-mixin";
9-
import {
10-
setupMediaQueryListeners,
11-
setupTimeListeners,
12-
} from "../../../common/condition/listeners";
139
import { migrateLayoutToGridOptions } from "../common/compute-card-grid-size";
1410
import { computeCardSize } from "../common/compute-card-size";
1511
import { checkConditionsMet } from "../common/validate-condition";
@@ -248,30 +244,6 @@ export class HuiCard extends ConditionalListenerMixin(ReactiveElement) {
248244
}
249245
}
250246

251-
protected setupConditionalListeners() {
252-
if (!this.config?.visibility || !this.hass) {
253-
return;
254-
}
255-
256-
setupMediaQueryListeners(
257-
this.config.visibility,
258-
this.hass,
259-
(unsub) => this.addConditionalListener(unsub),
260-
(conditionsMet) => {
261-
this._updateVisibility(conditionsMet);
262-
}
263-
);
264-
265-
setupTimeListeners(
266-
this.config.visibility,
267-
this.hass,
268-
(unsub) => this.addConditionalListener(unsub),
269-
(conditionsMet) => {
270-
this._updateVisibility(conditionsMet);
271-
}
272-
);
273-
}
274-
275247
private _updateVisibility(ignoreConditions?: boolean) {
276248
if (!this._element || !this.hass) {
277249
return;

src/panels/lovelace/components/hui-conditional-base.ts

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ import { ReactiveElement } from "lit";
33
import { customElement, property, state } from "lit/decorators";
44
import type { HomeAssistant } from "../../../types";
55
import { ConditionalListenerMixin } from "../../../mixins/conditional-listener-mixin";
6-
import {
7-
setupMediaQueryListeners,
8-
setupTimeListeners,
9-
} from "../../../common/condition/listeners";
106
import type { HuiCard } from "../cards/hui-card";
117
import type { ConditionalCardConfig } from "../cards/types";
128
import type { Condition } from "../common/validate-condition";
@@ -74,27 +70,13 @@ export class HuiConditionalBase extends ConditionalListenerMixin(
7470
return;
7571
}
7672

73+
// Filter to supported conditions (those with 'condition' property)
7774
const supportedConditions = this._config.conditions.filter(
7875
(c) => "condition" in c
7976
) as Condition[];
8077

81-
setupMediaQueryListeners(
82-
supportedConditions,
83-
this.hass,
84-
(unsub) => this.addConditionalListener(unsub),
85-
(conditionsMet) => {
86-
this.setVisibility(conditionsMet);
87-
}
88-
);
89-
90-
setupTimeListeners(
91-
supportedConditions,
92-
this.hass,
93-
(unsub) => this.addConditionalListener(unsub),
94-
(conditionsMet) => {
95-
this.setVisibility(conditionsMet);
96-
}
97-
);
78+
// Pass filtered conditions to parent implementation
79+
super.setupConditionalListeners(supportedConditions);
9880
}
9981

10082
protected update(changed: PropertyValues): void {
@@ -112,17 +94,15 @@ export class HuiConditionalBase extends ConditionalListenerMixin(
11294
}
11395
}
11496

115-
private _updateVisibility() {
97+
private _updateVisibility(conditionsMet?: boolean) {
11698
if (!this._element || !this.hass || !this._config) {
11799
return;
118100
}
119101

120102
this._element.preview = this.preview;
121103

122-
const conditionMet = checkConditionsMet(
123-
this._config!.conditions,
124-
this.hass!
125-
);
104+
const conditionMet =
105+
conditionsMet ?? checkConditionsMet(this._config.conditions, this.hass);
126106

127107
this.setVisibility(conditionMet);
128108
}

src/panels/lovelace/heading-badges/hui-heading-badge.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ import { fireEvent } from "../../../common/dom/fire_event";
55
import "../../../components/ha-svg-icon";
66
import type { HomeAssistant } from "../../../types";
77
import { ConditionalListenerMixin } from "../../../mixins/conditional-listener-mixin";
8-
import {
9-
setupMediaQueryListeners,
10-
setupTimeListeners,
11-
} from "../../../common/condition/listeners";
128
import { checkConditionsMet } from "../common/validate-condition";
139
import { createHeadingBadgeElement } from "../create-element/create-heading-badge-element";
1410
import type { LovelaceHeadingBadge } from "../types";
@@ -134,30 +130,6 @@ export class HuiHeadingBadge extends ConditionalListenerMixin(ReactiveElement) {
134130
}
135131
}
136132

137-
protected setupConditionalListeners() {
138-
if (!this.config?.visibility || !this.hass) {
139-
return;
140-
}
141-
142-
setupMediaQueryListeners(
143-
this.config.visibility,
144-
this.hass,
145-
(unsub) => this.addConditionalListener(unsub),
146-
(conditionsMet) => {
147-
this._updateVisibility(conditionsMet);
148-
}
149-
);
150-
151-
setupTimeListeners(
152-
this.config.visibility,
153-
this.hass,
154-
(unsub) => this.addConditionalListener(unsub),
155-
(conditionsMet) => {
156-
this._updateVisibility(conditionsMet);
157-
}
158-
);
159-
}
160-
161133
private _updateVisibility(forceVisible?: boolean) {
162134
if (!this._element || !this.hass) {
163135
return;

src/panels/lovelace/sections/hui-section.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ import type {
1414
import { isStrategySection } from "../../../data/lovelace/config/section";
1515
import type { HomeAssistant } from "../../../types";
1616
import { ConditionalListenerMixin } from "../../../mixins/conditional-listener-mixin";
17-
import {
18-
setupMediaQueryListeners,
19-
setupTimeListeners,
20-
} from "../../../common/condition/listeners";
2117
import "../cards/hui-card";
2218
import type { HuiCard } from "../cards/hui-card";
2319
import { checkConditionsMet } from "../common/validate-condition";
@@ -153,30 +149,6 @@ export class HuiSection extends ConditionalListenerMixin(ReactiveElement) {
153149
}
154150
}
155151

156-
protected setupConditionalListeners() {
157-
if (!this._config?.visibility || !this.hass) {
158-
return;
159-
}
160-
161-
setupMediaQueryListeners(
162-
this._config.visibility,
163-
this.hass,
164-
(unsub) => this.addConditionalListener(unsub),
165-
(conditionsMet) => {
166-
this._updateElement(conditionsMet);
167-
}
168-
);
169-
170-
setupTimeListeners(
171-
this._config.visibility,
172-
this.hass,
173-
(unsub) => this.addConditionalListener(unsub),
174-
(conditionsMet) => {
175-
this._updateElement(conditionsMet);
176-
}
177-
);
178-
}
179-
180152
private async _initializeConfig() {
181153
let sectionConfig = { ...this.config };
182154
let isStrategy = false;

0 commit comments

Comments
 (0)