Skip to content

Commit c84f9dd

Browse files
Merge pull request #6 from sergiocarracedo/feature/area-card-variants
feat: add compact and mini variants for area card
2 parents e6db999 + 3be06c1 commit c84f9dd

File tree

17 files changed

+243
-45
lines changed

17 files changed

+243
-45
lines changed

README.md

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ Using `style: header` the card will render in a header style, showing the room n
113113

114114
![img_1.png](docs/img_1.png)
115115

116+
**Compact variant** (smaller sizes, ideal for dashboards with many cards):
117+
118+
![Area Card Compact](docs/area-card-compact.png)
119+
120+
**Mini variant** (even smaller, perfect for compact layouts):
121+
122+
![Area Card Mini](docs/area-card-mini.png)
123+
116124
### Features
117125

118126
- Displays room-specific data such as temperature, humidity, and device statuses.
@@ -126,22 +134,23 @@ To use the Room Card, add it to your Lovelace dashboard. `type: custom:sc-area-c
126134

127135
#### Configuration Options
128136

129-
| Option | Type | Required | Description |
130-
| ----------------------------- | -------------- | -------- | --------------------------------------------------------------------- |
131-
| `area` | `string` | Yes | The area or room to display information for. |
132-
| `style` | `string` | No | The style of the card (e.g., `header`, `full`). (`full` by default) |
133-
| `color` | `string` | No | The color of the card (e.g., `#f90`, `var(--green)`). |
134-
| `summary` | `array` | No | A list of entity groups to summarize (e.g., lights, doors, windows). |
135-
| `summary[].name` | `string` | Yes | The name of the entity group. |
136-
| `summary[].icon` | `string` | Yes | The icon to represent the entity group. |
137-
| `summary[].entities` | `array` | Yes | A list of entity IDs for the group. |
138-
| `summary[].alarm_entities` | `array` | No | A list of of entities to show as alarms. |
139-
| `summary[].tap_action` | `ActionConfig` | No | Action to perform on tap in summary (e.g., navigate to another view). |
140-
| `summary[].hold_action` | `ActionConfig` | No | Action to perform on hold in summary (e.g., show more details). |
141-
| `summary[].double_tap_action` | `ActionConfig` | No | Action to perform on double tap in summary(e.g., toggle a device). |
142-
| `tap_action` | `ActionConfig` | No | Action to perform on tap in card (e.g., navigate to another view). |
143-
| `hold_action` | `ActionConfig` | No | Action to perform on hold in card (e.g., show more details). |
144-
| `double_tap_action` | `ActionConfig` | No | Action to perform on double tap in card (e.g., toggle a device). |
137+
| Option | Type | Required | Description |
138+
| ----------------------------- | -------------- | -------- | ----------------------------------------------------------------------------------- |
139+
| `area` | `string` | Yes | The area or room to display information for. |
140+
| `style` | `string` | No | The style of the card (e.g., `header`, `full`). (`full` by default) |
141+
| `variant` | `string` | No | The size variant of the card (`default`, `compact`, `mini`). (`default` by default) |
142+
| `color` | `string` | No | The color of the card (e.g., `#f90`, `var(--green)`). |
143+
| `summary` | `array` | No | A list of entity groups to summarize (e.g., lights, doors, windows). |
144+
| `summary[].name` | `string` | Yes | The name of the entity group. |
145+
| `summary[].icon` | `string` | Yes | The icon to represent the entity group. |
146+
| `summary[].entities` | `array` | Yes | A list of entity IDs for the group. |
147+
| `summary[].alarm_entities` | `array` | No | A list of of entities to show as alarms. |
148+
| `summary[].tap_action` | `ActionConfig` | No | Action to perform on tap in summary (e.g., navigate to another view). |
149+
| `summary[].hold_action` | `ActionConfig` | No | Action to perform on hold in summary (e.g., show more details). |
150+
| `summary[].double_tap_action` | `ActionConfig` | No | Action to perform on double tap in summary(e.g., toggle a device). |
151+
| `tap_action` | `ActionConfig` | No | Action to perform on tap in card (e.g., navigate to another view). |
152+
| `hold_action` | `ActionConfig` | No | Action to perform on hold in card (e.g., show more details). |
153+
| `double_tap_action` | `ActionConfig` | No | Action to perform on double tap in card (e.g., toggle a device). |
145154

146155
The cards provides predefined entity groups for `lights`, `doors`, `windows`, and `alarms`.
147156

@@ -196,6 +205,21 @@ presence:
196205
2. **Bedroom Control**:
197206
Display the status of alarms, lights, and environmental sensors in the bedroom.
198207

208+
**Using variants for compact layouts:**
209+
210+
```yaml
211+
type: custom:sc-area-card
212+
area: kitchen
213+
variant: compact # or 'mini' for even smaller
214+
style: header
215+
summary:
216+
- name: Appliances
217+
icon: mdi:fridge
218+
entities:
219+
- switch.kitchen_fridge
220+
- switch.kitchen_microwave
221+
```
222+
199223
## History Bars Card
200224

201225
The History Bars Card is a custom Lovelace card for Home Assistant that displays historical data in a bar

docs/area-card-compact.png

27.4 KB
Loading

docs/area-card-mini.png

20.1 KB
Loading

docs/area-card-variants.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
**Compact variant** (smaller sizes, ideal for dashboards with many cards):
2+
3+
![Area Card Compact](docs/area-card-compact.png)
4+
5+
**Mini variant** (even smaller, perfect for compact layouts):
6+
7+
![Area Card Mini](docs/area-card-mini.png)

src/area-card/AreaCard.ts

Lines changed: 147 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,69 @@
1-
import { css, html, LitElement, nothing, type TemplateResult } from 'lit'
2-
import { classMap } from 'lit/directives/class-map.js'
3-
import { customElement, state } from 'lit/decorators.js'
1+
import { actionHandler } from '@/action-handler-directive'
2+
import { areaColors } from '@/area-colors'
3+
import type { Area } from '@/types.ts'
4+
import { toArray } from '@/utils'
45
import {
56
type ActionHandlerEvent,
67
handleAction,
78
hasAction,
89
type HomeAssistant,
910
} from 'custom-card-helpers'
10-
import type { Area } from '@/types.ts'
11-
import { areaColors } from '@/area-colors'
12-
import type { EntityTypeSummary, ScAreaCardConfig } from './types.ts'
13-
import { actionHandler } from '@/action-handler-directive'
14-
import './TempHum.ts'
11+
import { css, html, LitElement, nothing, type TemplateResult } from 'lit'
12+
import { customElement, state } from 'lit/decorators.js'
13+
import { classMap } from 'lit/directives/class-map.js'
1514
import '../components/Icon.ts'
16-
import './TempHumChart.ts'
1715
import './EntityTypeStatus.ts'
18-
import { toArray } from '@/utils'
16+
import './TempHum.ts'
17+
import './TempHumChart.ts'
18+
import type { EntityTypeSummary, ScAreaCardConfig } from './types.ts'
19+
20+
const VARIANT_SIZES = {
21+
default: {
22+
titleFontSize: 20,
23+
titleFontSizeHeader: 30,
24+
tempFontSize: 28,
25+
humFontSize: 15,
26+
areaIconSize: 100,
27+
areaIconIconSize: 40,
28+
summaryIconSize: 36,
29+
statusGap: 10,
30+
iconLeft: -10,
31+
iconBottom: -10,
32+
chartHeight: 100,
33+
borderRadius: 12,
34+
padding: '12px 16px',
35+
},
36+
compact: {
37+
titleFontSize: 16,
38+
titleFontSizeHeader: 24,
39+
tempFontSize: 22,
40+
humFontSize: 12,
41+
areaIconSize: 60,
42+
areaIconIconSize: 28,
43+
summaryIconSize: 30,
44+
statusGap: 7,
45+
iconLeft: 0,
46+
iconBottom: -5,
47+
chartHeight: 60,
48+
borderRadius: 10,
49+
padding: '10px 12px',
50+
},
51+
mini: {
52+
titleFontSize: 14,
53+
titleFontSizeHeader: 22,
54+
tempFontSize: 18,
55+
humFontSize: 12,
56+
areaIconSize: 45,
57+
areaIconIconSize: 20,
58+
summaryIconSize: 26,
59+
statusGap: 5,
60+
iconLeft: 5,
61+
iconBottom: -5,
62+
chartHeight: 40,
63+
borderRadius: 8,
64+
padding: '8px 10px',
65+
},
66+
}
1967

2068
@customElement('sc-area-card')
2169
export class ScAreaCard extends LitElement {
@@ -113,6 +161,14 @@ export class ScAreaCard extends LitElement {
113161
})
114162
}
115163

164+
get variant() {
165+
return this._config?.variant || 'default'
166+
}
167+
168+
get variantSizes() {
169+
return VARIANT_SIZES[this.variant]
170+
}
171+
116172
private handleAction(ev: ActionHandlerEvent) {
117173
if (!this._config || !this._hass) return
118174
handleAction(
@@ -145,12 +201,15 @@ export class ScAreaCard extends LitElement {
145201
return html`<p class="error">${this._config.area} is unavailable.</p>`
146202
}
147203

204+
const sizes = this.variantSizes
205+
148206
const header: TemplateResult = html`<header class="area-card__header">
149207
<h1 class="area-card__name">${area.name}</h1>
150208
<temp-hum
151209
.temperatureEntityId=${area.temperature_entity_id}
152210
.humidityEntityId=${area.humidity_entity_id}
153211
.hass=${this._hass}
212+
.fontSize=${sizes.tempFontSize}
154213
></temp-hum>
155214
</header>`
156215

@@ -159,6 +218,7 @@ export class ScAreaCard extends LitElement {
159218
'area-card--alarm': this.alarmActive,
160219
'area-card--with-action': this.hasAction,
161220
[`area-card--style-${this._config.style || 'full'}`]: true,
221+
[`area-card--variant-${this.variant}`]: true,
162222
}
163223
const summaries = this.summaryTypes.filter((type) => type.entities.length)
164224

@@ -171,8 +231,24 @@ export class ScAreaCard extends LitElement {
171231
hasDoubleClick: hasAction(this._config.double_tap_action),
172232
})}
173233
class="${classMap(classes)}"
234+
style="--border-radius: ${sizes.borderRadius}px; --padding: ${sizes.padding};"
174235
>
175-
<div class="area-card__content" style="--area-color: ${this.areaColor};">
236+
<div
237+
class="area-card__content"
238+
style="
239+
--area-color: ${this.areaColor};
240+
--title-font-size: ${sizes.titleFontSize}px;
241+
--title-font-size-header: ${sizes.titleFontSizeHeader}px;
242+
--temp-font-size: ${sizes.tempFontSize}px;
243+
--hum-font-size: ${sizes.humFontSize}px;
244+
--area-icon-size: ${sizes.areaIconSize}px;
245+
--area-icon-icon-size: ${sizes.areaIconIconSize}px;
246+
--summary-icon-size: ${sizes.summaryIconSize}px;
247+
--status-gap: ${sizes.statusGap}px;
248+
--icon-left: ${sizes.iconLeft}px;
249+
--icon-bottom: ${sizes.iconBottom}px;
250+
"
251+
>
176252
${header}
177253
${summaries.length
178254
? html`<aside class="area-card__status">
@@ -185,6 +261,7 @@ export class ScAreaCard extends LitElement {
185261
.name=${type.name}
186262
.bgColor=${this.areaColor}
187263
.color=${'var(--black2)'}
264+
.size=${sizes.summaryIconSize}
188265
.actions=${{
189266
tap_action: type.tap_action,
190267
hold_action: type.hold_action,
@@ -200,8 +277,8 @@ export class ScAreaCard extends LitElement {
200277
class="area-card__icon"
201278
.icon=${area.icon}
202279
.color=${this.areaColor}
203-
size="100"
204-
iconSize="40"
280+
.size=${sizes.areaIconSize}
281+
.iconSize=${sizes.areaIconIconSize}
205282
></sc-icon>
206283
${(area.temperature_entity_id || area.humidity_entity_id) &&
207284
html`<div class="area-card__chart">
@@ -210,6 +287,7 @@ export class ScAreaCard extends LitElement {
210287
.temperatureEntityId=${area.temperature_entity_id}
211288
.humidityEntityId=${area.humidity_entity_id}
212289
.color="${this.areaColor}"
290+
.height=${sizes.chartHeight}
213291
></temp-hum-chart>
214292
</div>`}
215293
</div>
@@ -232,8 +310,8 @@ export class ScAreaCard extends LitElement {
232310
.area-card {
233311
position: relative;
234312
overflow: hidden;
235-
border-radius: var(--ha-card-border-radius, 12px);
236-
padding: 12px 16px;
313+
border-radius: var(--border-radius, var(--ha-card-border-radius, 12px));
314+
padding: var(--padding, 12px 16px);
237315
}
238316
239317
.area-card--alarm {
@@ -258,13 +336,26 @@ export class ScAreaCard extends LitElement {
258336
display: flex;
259337
flex-direction: column;
260338
justify-content: flex-end;
339+
overflow: hidden;
261340
temp-hum-chart {
262341
max-height: 100px;
263342
}
264343
}
265344
.area-card--style-header .area-card__chart {
266345
opacity: 0.7;
267346
}
347+
.area-card--variant-compact .area-card__chart {
348+
top: 20px;
349+
}
350+
.area-card--variant-compact .area-card__chart temp-hum-chart {
351+
max-height: 60px;
352+
}
353+
.area-card--variant-mini .area-card__chart {
354+
top: 15px;
355+
}
356+
.area-card--variant-mini .area-card__chart temp-hum-chart {
357+
max-height: 40px;
358+
}
268359
269360
.area-card__content {
270361
min-height: 120px;
@@ -278,6 +369,17 @@ export class ScAreaCard extends LitElement {
278369
justify-content: center;
279370
}
280371
372+
.area-card--variant-compact {
373+
height: 90px;
374+
}
375+
.area-card--variant-mini {
376+
height: 70px;
377+
}
378+
.area-card--variant-compact.area-card--style-header .area-card__content,
379+
.area-card--variant-mini.area-card--style-header .area-card__content {
380+
padding-left: 70px;
381+
}
382+
281383
.area-card__header {
282384
display: flex;
283385
gap: 0 10px;
@@ -290,13 +392,13 @@ export class ScAreaCard extends LitElement {
290392
}
291393
292394
.area-card__name {
293-
font-size: 20px;
395+
font-size: var(--title-font-size, 20px);
294396
margin: 0;
295397
font-weight: 300;
296398
}
297399
298400
.area-card--style-header .area-card__name {
299-
font-size: 30px;
401+
font-size: var(--title-font-size-header, 30px);
300402
font-weight: 200;
301403
}
302404
@@ -306,7 +408,7 @@ export class ScAreaCard extends LitElement {
306408
justify-content: flex-end;
307409
align-items: flex-end;
308410
padding-top: 40px;
309-
gap: 10px;
411+
gap: var(--status-gap, 10px);
310412
z-index: 2;
311413
flex: 1;
312414
margin-left: 80px;
@@ -315,10 +417,15 @@ export class ScAreaCard extends LitElement {
315417
padding-top: 10px;
316418
}
317419
420+
.area-card--variant-compact .area-card__status,
421+
.area-card--variant-mini .area-card__status {
422+
margin-left: 0;
423+
}
424+
318425
.area-card__icon {
319426
position: absolute;
320-
left: -10px;
321-
bottom: -10px;
427+
left: var(--icon-left, -10px);
428+
bottom: var(--icon-bottom, -10px);
322429
z-index: 2;
323430
pointer-events: none;
324431
display: flex;
@@ -353,5 +460,25 @@ export class ScAreaCard extends LitElement {
353460
background-color: var(--ha-card-background, white);
354461
}
355462
}
463+
464+
/* Card editor preview adjustments */
465+
hui-card-edit-mode .area-card {
466+
min-height: var(--min-height, 120px);
467+
}
468+
hui-card-edit-mode .area-card--style-header {
469+
min-height: var(--min-height-header, 50px);
470+
}
471+
hui-card-edit-mode .area-card--variant-compact {
472+
min-height: 90px;
473+
}
474+
hui-card-edit-mode .area-card--variant-compact.area-card--style-header {
475+
min-height: 70px;
476+
}
477+
hui-card-edit-mode .area-card--variant-mini {
478+
min-height: 55px;
479+
}
480+
hui-card-edit-mode .area-card--variant-mini.area-card--style-header {
481+
min-height: 45px;
482+
}
356483
`
357484
}

0 commit comments

Comments
 (0)