mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-18 14:56:37 +00:00
Add tile card to gallery (#15394)
This commit is contained in:
parent
6c2a767896
commit
7e92d62936
3
gallery/src/pages/lovelace/tile-card.markdown
Normal file
3
gallery/src/pages/lovelace/tile-card.markdown
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
---
|
||||||
|
title: Tile Card
|
||||||
|
---
|
173
gallery/src/pages/lovelace/tile-card.ts
Normal file
173
gallery/src/pages/lovelace/tile-card.ts
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||||
|
import { customElement, query } from "lit/decorators";
|
||||||
|
import { CoverEntityFeature } from "../../../../src/data/cover";
|
||||||
|
import { LightColorMode } from "../../../../src/data/light";
|
||||||
|
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
|
||||||
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
|
import "../../components/demo-cards";
|
||||||
|
|
||||||
|
const ENTITIES = [
|
||||||
|
getEntity("switch", "tv_outlet", "on", {
|
||||||
|
friendly_name: "TV outlet",
|
||||||
|
device_class: "outlet",
|
||||||
|
}),
|
||||||
|
getEntity("light", "bed_light", "on", {
|
||||||
|
friendly_name: "Bed Light",
|
||||||
|
supported_color_modes: [LightColorMode.HS],
|
||||||
|
}),
|
||||||
|
getEntity("light", "unavailable", "unavailable", {
|
||||||
|
friendly_name: "Unavailable entity",
|
||||||
|
}),
|
||||||
|
getEntity("climate", "thermostat", "heat", {
|
||||||
|
current_temperature: 73,
|
||||||
|
min_temp: 45,
|
||||||
|
max_temp: 95,
|
||||||
|
temperature: 80,
|
||||||
|
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||||
|
friendly_name: "Thermostat",
|
||||||
|
hvac_action: "heating",
|
||||||
|
}),
|
||||||
|
getEntity("person", "paulus", "home", {
|
||||||
|
friendly_name: "Paulus",
|
||||||
|
}),
|
||||||
|
getEntity("vacuum", "first_floor_vacuum", "docked", {
|
||||||
|
friendly_name: "First floor vacuum",
|
||||||
|
supported_features:
|
||||||
|
VacuumEntityFeature.START +
|
||||||
|
VacuumEntityFeature.STOP +
|
||||||
|
VacuumEntityFeature.RETURN_HOME,
|
||||||
|
}),
|
||||||
|
getEntity("cover", "kitchen_shutter", "open", {
|
||||||
|
friendly_name: "Kitchen shutter",
|
||||||
|
device_class: "shutter",
|
||||||
|
supported_features:
|
||||||
|
CoverEntityFeature.CLOSE +
|
||||||
|
CoverEntityFeature.OPEN +
|
||||||
|
CoverEntityFeature.STOP,
|
||||||
|
}),
|
||||||
|
getEntity("cover", "pergola_roof", "open", {
|
||||||
|
friendly_name: "Pergola Roof",
|
||||||
|
supported_features:
|
||||||
|
CoverEntityFeature.CLOSE_TILT +
|
||||||
|
CoverEntityFeature.OPEN_TILT +
|
||||||
|
CoverEntityFeature.STOP_TILT,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const CONFIGS = [
|
||||||
|
{
|
||||||
|
heading: "Basic example",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: switch.tv_outlet
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Vertical example",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: switch.tv_outlet
|
||||||
|
vertical: true
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Custom color",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: switch.tv_outlet
|
||||||
|
color: pink
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Unknown entity",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: light.unknown
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Unavailable entity",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: light.unavailable
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Climate",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: climate.thermostat
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Person",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: person.paulus
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Light brightness feature",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: light.bed_light
|
||||||
|
features:
|
||||||
|
- type: "light-brightness"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Vacuum commands feature",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: vacuum.first_floor_vacuum
|
||||||
|
features:
|
||||||
|
- type: "vacuum-commands"
|
||||||
|
commands:
|
||||||
|
- start_pause
|
||||||
|
- stop
|
||||||
|
- return_home
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Cover open close feature",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: cover.kitchen_shutter
|
||||||
|
features:
|
||||||
|
- type: "cover-open-close"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
heading: "Cover tilt feature",
|
||||||
|
config: `
|
||||||
|
- type: tile
|
||||||
|
entity: cover.pergola_roof
|
||||||
|
features:
|
||||||
|
- type: "cover-tilt"
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@customElement("demo-lovelace-tile-card")
|
||||||
|
class DemoTile extends LitElement {
|
||||||
|
@query("#demos") private _demoRoot!: HTMLElement;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProperties: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProperties);
|
||||||
|
const hass = provideHass(this._demoRoot);
|
||||||
|
hass.updateTranslations(null, "en");
|
||||||
|
hass.updateTranslations("lovelace", "en");
|
||||||
|
hass.addEntities(ENTITIES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"demo-lovelace-tile-card": DemoTile;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,8 @@ export class HaTileInfo extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 40px;
|
||||||
}
|
}
|
||||||
span {
|
span {
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -16,7 +16,6 @@ import memoizeOne from "memoize-one";
|
|||||||
import { computeCssColor } from "../../../common/color/compute-color";
|
import { computeCssColor } from "../../../common/color/compute-color";
|
||||||
import { hsv2rgb, rgb2hex, rgb2hsv } from "../../../common/color/convert-color";
|
import { hsv2rgb, rgb2hex, rgb2hsv } from "../../../common/color/convert-color";
|
||||||
import { DOMAINS_TOGGLE } from "../../../common/const";
|
import { DOMAINS_TOGGLE } from "../../../common/const";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
|
||||||
import { stateActive } from "../../../common/entity/state_active";
|
import { stateActive } from "../../../common/entity/state_active";
|
||||||
@ -263,27 +262,29 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
const entityId = this._config.entity;
|
const entityId = this._config.entity;
|
||||||
const stateObj = entityId ? this.hass.states[entityId] : undefined;
|
const stateObj = entityId ? this.hass.states[entityId] : undefined;
|
||||||
|
|
||||||
const tileClasses = { vertical: Boolean(this._config.vertical) };
|
const contentClasses = { vertical: Boolean(this._config.vertical) };
|
||||||
|
|
||||||
if (!stateObj) {
|
if (!stateObj) {
|
||||||
return html`
|
return html`
|
||||||
<ha-card class="disabled">
|
<ha-card>
|
||||||
<div class="tile ${classMap(tileClasses)}">
|
<div class="tile">
|
||||||
<div class="icon-container">
|
<div class="content ${classMap(contentClasses)}">
|
||||||
<ha-tile-icon class="icon" .iconPath=${mdiHelp}></ha-tile-icon>
|
<div class="icon-container">
|
||||||
<ha-tile-badge
|
<ha-tile-icon class="icon" .iconPath=${mdiHelp}></ha-tile-icon>
|
||||||
class="badge"
|
<ha-tile-badge
|
||||||
.iconPath=${mdiExclamationThick}
|
class="badge"
|
||||||
style=${styleMap({
|
.iconPath=${mdiExclamationThick}
|
||||||
"--tile-badge-background-color": `var(--red-color)`,
|
style=${styleMap({
|
||||||
})}
|
"--tile-badge-background-color": `var(--red-color)`,
|
||||||
></ha-tile-badge>
|
})}
|
||||||
|
></ha-tile-badge>
|
||||||
|
</div>
|
||||||
|
<ha-tile-info
|
||||||
|
class="info"
|
||||||
|
.primary=${entityId}
|
||||||
|
secondary=${this.hass.localize("ui.card.tile.not_found")}
|
||||||
|
></ha-tile-info>
|
||||||
</div>
|
</div>
|
||||||
<ha-tile-info
|
|
||||||
class="info"
|
|
||||||
.primary=${entityId}
|
|
||||||
secondary=${this.hass.localize("ui.card.tile.not_found")}
|
|
||||||
></ha-tile-info>
|
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
@ -315,66 +316,62 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
return html`
|
return html`
|
||||||
<ha-card style=${styleMap(style)} class=${classMap({ active })}>
|
<ha-card style=${styleMap(style)} class=${classMap({ active })}>
|
||||||
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : null}
|
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : null}
|
||||||
<div
|
<div class="tile">
|
||||||
class="tile ${classMap(tileClasses)}"
|
|
||||||
@action=${this._handleAction}
|
|
||||||
.actionHandler=${actionHandler()}
|
|
||||||
role="button"
|
|
||||||
tabindex="0"
|
|
||||||
@mousedown=${this.handleRippleActivate}
|
|
||||||
@mouseup=${this.handleRippleDeactivate}
|
|
||||||
@mouseenter=${this.handleRippleMouseEnter}
|
|
||||||
@mouseleave=${this.handleRippleMouseLeave}
|
|
||||||
@touchstart=${this.handleRippleActivate}
|
|
||||||
@touchend=${this.handleRippleDeactivate}
|
|
||||||
@touchcancel=${this.handleRippleDeactivate}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="icon-container"
|
class="background"
|
||||||
|
@action=${this._handleAction}
|
||||||
|
.actionHandler=${actionHandler()}
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@action=${this._handleIconAction}
|
@mousedown=${this.handleRippleActivate}
|
||||||
.actionHandler=${actionHandler()}
|
@mouseup=${this.handleRippleDeactivate}
|
||||||
@mousedown=${stopPropagation}
|
@mouseenter=${this.handleRippleMouseEnter}
|
||||||
@mouseup=${stopPropagation}
|
@mouseleave=${this.handleRippleMouseLeave}
|
||||||
@mouseenter=${stopPropagation}
|
@touchstart=${this.handleRippleActivate}
|
||||||
@mouseleave=${stopPropagation}
|
@touchend=${this.handleRippleDeactivate}
|
||||||
@touchstart=${stopPropagation}
|
@touchcancel=${this.handleRippleDeactivate}
|
||||||
@touchend=${stopPropagation}
|
></div>
|
||||||
@touchcancel=${stopPropagation}
|
<div class="content ${classMap(contentClasses)}">
|
||||||
>
|
<div
|
||||||
${imageUrl
|
class="icon-container"
|
||||||
? html`
|
role="button"
|
||||||
<ha-tile-image
|
tabindex="0"
|
||||||
class="icon"
|
@action=${this._handleIconAction}
|
||||||
.imageUrl=${imageUrl}
|
.actionHandler=${actionHandler()}
|
||||||
></ha-tile-image>
|
>
|
||||||
`
|
${imageUrl
|
||||||
: html`
|
? html`
|
||||||
<ha-tile-icon
|
<ha-tile-image
|
||||||
class="icon"
|
class="icon"
|
||||||
.icon=${icon}
|
.imageUrl=${imageUrl}
|
||||||
.iconPath=${iconPath}
|
></ha-tile-image>
|
||||||
></ha-tile-icon>
|
`
|
||||||
`}
|
: html`
|
||||||
${badge
|
<ha-tile-icon
|
||||||
? html`
|
class="icon"
|
||||||
<ha-tile-badge
|
.icon=${icon}
|
||||||
class="badge"
|
.iconPath=${iconPath}
|
||||||
.icon=${badge.icon}
|
></ha-tile-icon>
|
||||||
.iconPath=${badge.iconPath}
|
`}
|
||||||
style=${styleMap({
|
${badge
|
||||||
"--tile-badge-background-color": badge.color,
|
? html`
|
||||||
})}
|
<ha-tile-badge
|
||||||
></ha-tile-badge>
|
class="badge"
|
||||||
`
|
.icon=${badge.icon}
|
||||||
: null}
|
.iconPath=${badge.iconPath}
|
||||||
|
style=${styleMap({
|
||||||
|
"--tile-badge-background-color": badge.color,
|
||||||
|
})}
|
||||||
|
></ha-tile-badge>
|
||||||
|
`
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
<ha-tile-info
|
||||||
|
class="info"
|
||||||
|
.primary=${name}
|
||||||
|
.secondary=${stateDisplay}
|
||||||
|
></ha-tile-info>
|
||||||
</div>
|
</div>
|
||||||
<ha-tile-info
|
|
||||||
class="info"
|
|
||||||
.primary=${name}
|
|
||||||
.secondary=${stateDisplay}
|
|
||||||
></ha-tile-info>
|
|
||||||
</div>
|
</div>
|
||||||
${supportedFeatures?.length
|
${supportedFeatures?.length
|
||||||
? html`
|
? html`
|
||||||
@ -424,16 +421,15 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
--tile-color: var(--state-inactive-color);
|
--tile-color: var(--state-inactive-color);
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
ha-card:has(.tile:focus-visible) {
|
ha-card:has(.background:focus-visible) {
|
||||||
border-color: var(--tile-color);
|
border-color: var(--tile-color);
|
||||||
box-shadow: 0 0 0 1px var(--tile-color);
|
box-shadow: 0 0 0 1px var(--tile-color);
|
||||||
}
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
--mdc-ripple-color: var(--tile-color);
|
--mdc-ripple-color: var(--tile-color);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
|
||||||
// For safari overflow hidden
|
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
ha-card.active {
|
ha-card.active {
|
||||||
--tile-color: var(--state-icon-color);
|
--tile-color: var(--state-icon-color);
|
||||||
@ -444,7 +440,14 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
[role="button"]:focus {
|
[role="button"]:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
.tile {
|
.background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -473,6 +476,10 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
.icon-container .icon {
|
.icon-container .icon {
|
||||||
--tile-icon-color: var(--tile-color);
|
--tile-icon-color: var(--tile-color);
|
||||||
|
user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
}
|
}
|
||||||
.icon-container .badge {
|
.icon-container .badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -488,9 +495,9 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 40px;
|
|
||||||
transition: background-color 180ms ease-in-out;
|
transition: background-color 180ms ease-in-out;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user