mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-11 12:01:07 +00:00
Compare commits
5 Commits
copilot/fi
...
reusable_t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a4a6e60fb | ||
|
|
d377275bff | ||
|
|
65d15da469 | ||
|
|
903ab67604 | ||
|
|
948b020b7c |
@@ -1,23 +1,17 @@
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
|
||||||
import { styleMap } from "lit/directives/style-map";
|
|
||||||
import { computeCssColor } from "../../../common/color/compute-color";
|
import { computeCssColor } from "../../../common/color/compute-color";
|
||||||
import { computeDomain } from "../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../common/entity/compute_domain";
|
||||||
import { generateEntityFilter } from "../../../common/entity/entity_filter";
|
import { generateEntityFilter } from "../../../common/entity/entity_filter";
|
||||||
import { formatNumber } from "../../../common/number/format_number";
|
import { formatNumber } from "../../../common/number/format_number";
|
||||||
import "../../../components/ha-card";
|
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../../../components/ha-ripple";
|
|
||||||
import "../../../components/tile/ha-tile-icon";
|
import "../../../components/tile/ha-tile-icon";
|
||||||
import "../../../components/tile/ha-tile-info";
|
import "../../../components/tile/ha-tile-info";
|
||||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
import "../../../state-display/state-display";
|
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction } from "../common/has-action";
|
||||||
|
import "../components/hui-tile";
|
||||||
import {
|
import {
|
||||||
findEntities,
|
findEntities,
|
||||||
getSummaryLabel,
|
getSummaryLabel,
|
||||||
@@ -229,140 +223,32 @@ export class HuiHomeSummaryCard extends LitElement implements LovelaceCard {
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentClasses = { vertical: Boolean(this._config.vertical) };
|
|
||||||
|
|
||||||
const color = computeCssColor(COLORS[this._config.summary]);
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
"--tile-color": color,
|
|
||||||
};
|
|
||||||
|
|
||||||
const secondary = this._computeSummaryState();
|
|
||||||
|
|
||||||
const label = getSummaryLabel(this.hass.localize, this._config.summary);
|
const label = getSummaryLabel(this.hass.localize, this._config.summary);
|
||||||
const icon = HOME_SUMMARIES_ICONS[this._config.summary];
|
const icon = HOME_SUMMARIES_ICONS[this._config.summary];
|
||||||
|
const color = computeCssColor(COLORS[this._config.summary]);
|
||||||
|
const secondary = this._computeSummaryState();
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card style=${styleMap(style)}>
|
<hui-tile
|
||||||
<div
|
name=${label}
|
||||||
class="background"
|
?vertical=${this._config.vertical}
|
||||||
@action=${this._handleAction}
|
color=${color}
|
||||||
.actionHandler=${actionHandler({
|
.hasCardAction=${this._hasCardAction}
|
||||||
hasHold: hasAction(this._config!.hold_action),
|
.onAction=${this._handleAction}
|
||||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
.tapAction=${this._config.tap_action}
|
||||||
})}
|
.holdAction=${this._config.hold_action}
|
||||||
role=${ifDefined(this._hasCardAction ? "button" : undefined)}
|
.doubleTapAction=${this._config.double_tap_action}
|
||||||
tabindex=${ifDefined(this._hasCardAction ? "0" : undefined)}
|
|
||||||
aria-labelledby="info"
|
|
||||||
>
|
>
|
||||||
<ha-ripple .disabled=${!this._hasCardAction}></ha-ripple>
|
<ha-tile-icon slot="icon">
|
||||||
</div>
|
|
||||||
<div class="container">
|
|
||||||
<div class="content ${classMap(contentClasses)}">
|
|
||||||
<ha-tile-icon>
|
|
||||||
<ha-icon slot="icon" .icon=${icon}></ha-icon>
|
<ha-icon slot="icon" .icon=${icon}></ha-icon>
|
||||||
</ha-tile-icon>
|
</ha-tile-icon>
|
||||||
<ha-tile-info
|
<ha-tile-info slot="info" id="info">
|
||||||
id="info"
|
<span slot="primary" class="primary">${label}</span>
|
||||||
.primary=${label}
|
<span slot="secondary">${secondary}</span>
|
||||||
.secondary=${secondary}
|
</ha-tile-info>
|
||||||
></ha-tile-info>
|
</hui-tile>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
|
||||||
:host {
|
|
||||||
--tile-color: var(--state-inactive-color);
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
ha-card:has(.background:focus-visible) {
|
|
||||||
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
|
||||||
--shadow-focus: 0 0 0 1px var(--tile-color);
|
|
||||||
border-color: var(--tile-color);
|
|
||||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
--ha-ripple-color: var(--tile-color);
|
|
||||||
--ha-ripple-hover-opacity: 0.04;
|
|
||||||
--ha-ripple-pressed-opacity: 0.12;
|
|
||||||
height: 100%;
|
|
||||||
transition:
|
|
||||||
box-shadow 180ms ease-in-out,
|
|
||||||
border-color 180ms ease-in-out;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
ha-card.active {
|
|
||||||
--tile-color: var(--state-icon-color);
|
|
||||||
}
|
|
||||||
[role="button"] {
|
|
||||||
cursor: pointer;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
[role="button"]:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
border-radius: var(--ha-card-border-radius, 12px);
|
|
||||||
margin: calc(-1 * var(--ha-card-border-width, 1px));
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
margin: calc(-1 * var(--ha-card-border-width, 1px));
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.container.horizontal {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
pointer-events: none;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vertical {
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.vertical ha-tile-info {
|
|
||||||
width: 100%;
|
|
||||||
flex: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-tile-icon {
|
|
||||||
--tile-icon-color: var(--tile-color);
|
|
||||||
position: relative;
|
|
||||||
padding: 6px;
|
|
||||||
margin: -6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-tile-info {
|
|
||||||
position: relative;
|
|
||||||
min-width: 0;
|
|
||||||
transition: background-color 180ms ease-in-out;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
|
||||||
import memoizeOne from "memoize-one";
|
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";
|
||||||
@@ -12,24 +11,15 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
|||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { stateActive } from "../../../common/entity/state_active";
|
import { stateActive } from "../../../common/entity/state_active";
|
||||||
import { stateColorCss } from "../../../common/entity/state_color";
|
import { stateColorCss } from "../../../common/entity/state_color";
|
||||||
import "../../../components/ha-card";
|
|
||||||
import "../../../components/ha-ripple";
|
|
||||||
import "../../../components/ha-state-icon";
|
|
||||||
import "../../../components/ha-svg-icon";
|
|
||||||
import "../../../components/tile/ha-tile-badge";
|
|
||||||
import "../../../components/tile/ha-tile-icon";
|
|
||||||
import "../../../components/tile/ha-tile-info";
|
|
||||||
import { cameraUrlWithWidthHeight } from "../../../data/camera";
|
import { cameraUrlWithWidthHeight } from "../../../data/camera";
|
||||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
import "../../../state-display/state-display";
|
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "../card-features/hui-card-features";
|
|
||||||
import type { LovelaceCardFeatureContext } from "../card-features/types";
|
import type { LovelaceCardFeatureContext } from "../card-features/types";
|
||||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
|
||||||
import { findEntities } from "../common/find-entities";
|
import { findEntities } from "../common/find-entities";
|
||||||
import { handleAction } from "../common/handle-action";
|
import { handleAction } from "../common/handle-action";
|
||||||
import { hasAction } from "../common/has-action";
|
import { hasAction } from "../common/has-action";
|
||||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||||
|
import "../components/hui-tile";
|
||||||
import type {
|
import type {
|
||||||
LovelaceCard,
|
LovelaceCard,
|
||||||
LovelaceCardEditor,
|
LovelaceCardEditor,
|
||||||
@@ -37,6 +27,7 @@ import type {
|
|||||||
} from "../types";
|
} from "../types";
|
||||||
import { renderTileBadge } from "./tile/badges/tile-badge";
|
import { renderTileBadge } from "./tile/badges/tile-badge";
|
||||||
import type { TileCardConfig } from "./types";
|
import type { TileCardConfig } from "./types";
|
||||||
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
|
|
||||||
export const getEntityDefaultTileIconAction = (entityId: string) => {
|
export const getEntityDefaultTileIconAction = (entityId: string) => {
|
||||||
const domain = computeDomain(entityId);
|
const domain = computeDomain(entityId);
|
||||||
@@ -253,10 +244,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentClasses = { vertical: Boolean(this._config.vertical) };
|
|
||||||
|
|
||||||
const name = this._config.name || computeStateName(stateObj);
|
const name = this._config.name || computeStateName(stateObj);
|
||||||
const active = stateActive(stateObj);
|
|
||||||
const color = this._computeStateColor(stateObj, this._config.color);
|
const color = this._computeStateColor(stateObj, this._config.color);
|
||||||
const domain = computeDomain(stateObj.entity_id);
|
const domain = computeDomain(stateObj.entity_id);
|
||||||
|
|
||||||
@@ -272,38 +260,26 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
</state-display>
|
</state-display>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const style = {
|
|
||||||
"--tile-color": color,
|
|
||||||
};
|
|
||||||
|
|
||||||
const imageUrl = this._config.show_entity_picture
|
const imageUrl = this._config.show_entity_picture
|
||||||
? this._getImageUrl(stateObj)
|
? this._getImageUrl(stateObj)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const featurePosition = this._featurePosition(this._config);
|
|
||||||
const features = this._displayedFeatures(this._config);
|
const features = this._displayedFeatures(this._config);
|
||||||
|
|
||||||
const containerOrientationClass =
|
|
||||||
featurePosition === "inline" ? "horizontal" : "";
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card style=${styleMap(style)} class=${classMap({ active })}>
|
<hui-tile
|
||||||
<div
|
?vertical=${this._config.vertical}
|
||||||
class="background"
|
color=${ifDefined(color)}
|
||||||
@action=${this._handleAction}
|
.hasCardAction=${this._hasCardAction}
|
||||||
.actionHandler=${actionHandler({
|
.hasIconAction=${this._hasIconAction}
|
||||||
hasHold: hasAction(this._config!.hold_action),
|
.onAction=${this._handleAction}
|
||||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
.tapAction=${this._config.tap_action}
|
||||||
})}
|
.holdAction=${this._config.hold_action}
|
||||||
role=${ifDefined(this._hasCardAction ? "button" : undefined)}
|
.doubleTapAction=${this._config.double_tap_action}
|
||||||
tabindex=${ifDefined(this._hasCardAction ? "0" : undefined)}
|
.featurePosition=${this._featurePosition(this._config)}
|
||||||
aria-labelledby="info"
|
|
||||||
>
|
>
|
||||||
<ha-ripple .disabled=${!this._hasCardAction}></ha-ripple>
|
|
||||||
</div>
|
|
||||||
<div class="container ${containerOrientationClass}">
|
|
||||||
<div class="content ${classMap(contentClasses)}">
|
|
||||||
<ha-tile-icon
|
<ha-tile-icon
|
||||||
|
slot="icon"
|
||||||
role=${ifDefined(this._hasIconAction ? "button" : undefined)}
|
role=${ifDefined(this._hasIconAction ? "button" : undefined)}
|
||||||
tabindex=${ifDefined(this._hasIconAction ? "0" : undefined)}
|
tabindex=${ifDefined(this._hasIconAction ? "0" : undefined)}
|
||||||
@action=${this._handleIconAction}
|
@action=${this._handleIconAction}
|
||||||
@@ -325,16 +301,16 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
${renderTileBadge(stateObj, this.hass)}
|
${renderTileBadge(stateObj, this.hass)}
|
||||||
</ha-tile-icon>
|
</ha-tile-icon>
|
||||||
<ha-tile-info id="info">
|
<ha-tile-info slot="info" id="info">
|
||||||
<span slot="primary" class="primary">${name}</span>
|
<span slot="primary" class="primary">${name}</span>
|
||||||
${stateDisplay
|
${stateDisplay
|
||||||
? html`<span slot="secondary">${stateDisplay}</span>`
|
? html`<span slot="secondary">${stateDisplay}</span>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-tile-info>
|
</ha-tile-info>
|
||||||
</div>
|
${features.length
|
||||||
${features.length > 0
|
|
||||||
? html`
|
? html`
|
||||||
<hui-card-features
|
<hui-card-features
|
||||||
|
slot="features"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.context=${this._featureContext}
|
.context=${this._featureContext}
|
||||||
.color=${this._config.color}
|
.color=${this._config.color}
|
||||||
@@ -342,92 +318,11 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
></hui-card-features>
|
></hui-card-features>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</hui-tile>
|
||||||
</ha-card>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
:host {
|
|
||||||
--tile-color: var(--state-inactive-color);
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
ha-card:has(.background:focus-visible) {
|
|
||||||
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
|
||||||
--shadow-focus: 0 0 0 1px var(--tile-color);
|
|
||||||
border-color: var(--tile-color);
|
|
||||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
--ha-ripple-color: var(--tile-color);
|
|
||||||
--ha-ripple-hover-opacity: 0.04;
|
|
||||||
--ha-ripple-pressed-opacity: 0.12;
|
|
||||||
height: 100%;
|
|
||||||
transition:
|
|
||||||
box-shadow 180ms ease-in-out,
|
|
||||||
border-color 180ms ease-in-out;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
ha-card.active {
|
|
||||||
--tile-color: var(--state-icon-color);
|
|
||||||
}
|
|
||||||
[role="button"] {
|
|
||||||
cursor: pointer;
|
|
||||||
pointer-events: auto;
|
|
||||||
}
|
|
||||||
[role="button"]:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
border-radius: var(--ha-card-border-radius, 12px);
|
|
||||||
margin: calc(-1 * var(--ha-card-border-width, 1px));
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
margin: calc(-1 * var(--ha-card-border-width, 1px));
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.container.horizontal {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
pointer-events: none;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vertical {
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.vertical ha-tile-info {
|
|
||||||
width: 100%;
|
|
||||||
flex: none;
|
|
||||||
}
|
|
||||||
ha-tile-icon {
|
|
||||||
--tile-icon-color: var(--tile-color);
|
|
||||||
position: relative;
|
|
||||||
padding: 6px;
|
|
||||||
margin: -6px;
|
|
||||||
}
|
|
||||||
ha-tile-badge {
|
ha-tile-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
@@ -435,23 +330,6 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
inset-inline-end: 3px;
|
inset-inline-end: 3px;
|
||||||
inset-inline-start: initial;
|
inset-inline-start: initial;
|
||||||
}
|
}
|
||||||
ha-tile-info {
|
|
||||||
position: relative;
|
|
||||||
min-width: 0;
|
|
||||||
transition: background-color 180ms ease-in-out;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
hui-card-features {
|
|
||||||
--feature-color: var(--tile-color);
|
|
||||||
padding: 0 12px 12px 12px;
|
|
||||||
}
|
|
||||||
.container.horizontal hui-card-features {
|
|
||||||
width: calc(50% - var(--column-gap, 0px) / 2 - 12px);
|
|
||||||
flex: none;
|
|
||||||
--feature-height: 36px;
|
|
||||||
padding: 0 12px;
|
|
||||||
padding-inline-start: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-tile-icon[data-domain="alarm_control_panel"][data-state="pending"],
|
ha-tile-icon[data-domain="alarm_control_panel"][data-state="pending"],
|
||||||
ha-tile-icon[data-domain="alarm_control_panel"][data-state="arming"],
|
ha-tile-icon[data-domain="alarm_control_panel"][data-state="arming"],
|
||||||
|
|||||||
181
src/panels/lovelace/components/hui-tile.ts
Normal file
181
src/panels/lovelace/components/hui-tile.ts
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import { LitElement, css, html } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import { styleMap } from "lit/directives/style-map";
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-ripple";
|
||||||
|
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
|
import { hasAction } from "../common/has-action";
|
||||||
|
import type { LovelaceCardFeaturePosition } from "../card-features/types";
|
||||||
|
|
||||||
|
@customElement("hui-tile")
|
||||||
|
export class HuiTile extends LitElement {
|
||||||
|
@property({ type: Boolean }) public vertical = false;
|
||||||
|
|
||||||
|
@property() public color?: string;
|
||||||
|
|
||||||
|
@property({ attribute: false, type: Boolean }) public hasCardAction = false;
|
||||||
|
|
||||||
|
@property({ attribute: false, type: Boolean }) public hasIconAction = false;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public onAction?: (
|
||||||
|
ev: ActionHandlerEvent
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public tapAction?: any;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public holdAction?: any;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public doubleTapAction?: any;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public featurePosition?: LovelaceCardFeaturePosition;
|
||||||
|
|
||||||
|
private _handleAction(ev: ActionHandlerEvent) {
|
||||||
|
this.onAction?.(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const contentClasses = { vertical: Boolean(this.vertical) };
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
"--tile-color": this.color,
|
||||||
|
};
|
||||||
|
|
||||||
|
const containerOrientationClass =
|
||||||
|
this.featurePosition === "inline" ? "horizontal" : "";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card style=${styleMap(style)}>
|
||||||
|
<div
|
||||||
|
class="background"
|
||||||
|
@action=${this._handleAction}
|
||||||
|
.actionHandler=${actionHandler({
|
||||||
|
hasHold: hasAction(this.holdAction),
|
||||||
|
hasDoubleClick: hasAction(this.doubleTapAction),
|
||||||
|
})}
|
||||||
|
role=${ifDefined(this.hasCardAction ? "button" : undefined)}
|
||||||
|
tabindex=${ifDefined(this.hasCardAction ? "0" : undefined)}
|
||||||
|
aria-labelledby="info"
|
||||||
|
>
|
||||||
|
<ha-ripple .disabled=${!this.hasCardAction}></ha-ripple>
|
||||||
|
</div>
|
||||||
|
<div class="container ${containerOrientationClass}">
|
||||||
|
<div class="content ${classMap(contentClasses)}">
|
||||||
|
<slot name="icon"></slot>
|
||||||
|
<slot name="info"></slot>
|
||||||
|
</div>
|
||||||
|
<slot name="features"></slot>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
--tile-color: var(--state-inactive-color);
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
ha-card:has(.background:focus-visible) {
|
||||||
|
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
||||||
|
--shadow-focus: 0 0 0 1px var(--tile-color);
|
||||||
|
border-color: var(--tile-color);
|
||||||
|
box-shadow: var(--shadow-default), var(--shadow-focus);
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
--ha-ripple-color: var(--tile-color);
|
||||||
|
--ha-ripple-hover-opacity: 0.04;
|
||||||
|
--ha-ripple-pressed-opacity: 0.12;
|
||||||
|
height: 100%;
|
||||||
|
transition:
|
||||||
|
box-shadow 180ms ease-in-out,
|
||||||
|
border-color 180ms ease-in-out;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
ha-card.active {
|
||||||
|
--tile-color: var(--state-icon-color);
|
||||||
|
}
|
||||||
|
[role="button"] {
|
||||||
|
cursor: pointer;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
[role="button"]:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
border-radius: var(--ha-card-border-radius, 12px);
|
||||||
|
margin: calc(-1 * var(--ha-card-border-width, 1px));
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
margin: calc(-1 * var(--ha-card-border-width, 1px));
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.container.horizontal {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: none;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical {
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.vertical ::slotted(ha-tile-info) {
|
||||||
|
width: 100%;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
::slotted(ha-tile-icon) {
|
||||||
|
--tile-icon-color: var(--tile-color);
|
||||||
|
position: relative;
|
||||||
|
padding: 6px;
|
||||||
|
margin: -6px;
|
||||||
|
}
|
||||||
|
::slotted(ha-tile-info) {
|
||||||
|
position: relative;
|
||||||
|
min-width: 0;
|
||||||
|
transition: background-color 180ms ease-in-out;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
::slotted(features) {
|
||||||
|
--feature-color: var(--tile-color);
|
||||||
|
padding: 0 12px 12px 12px;
|
||||||
|
}
|
||||||
|
.container.horizontal ::slotted(hui-card-features) {
|
||||||
|
width: calc(50% - var(--column-gap, 0px) / 2 - 12px);
|
||||||
|
flex: none;
|
||||||
|
--feature-height: 36px;
|
||||||
|
padding: 0 12px;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-tile": HuiTile;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user