mirror of
https://github.com/home-assistant/frontend.git
synced 2026-03-30 23:24:33 +00:00
Compare commits
9 Commits
dev
...
skeleton-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf519348b6 | ||
|
|
18872aef8a | ||
|
|
9dd7040e62 | ||
|
|
5192612dc9 | ||
|
|
9227b84494 | ||
|
|
4bfbc33f88 | ||
|
|
1b3a998c67 | ||
|
|
6a2bdf1c07 | ||
|
|
01acec1858 |
80
src/components/skeletons/skeleton-card-tile.ts
Normal file
80
src/components/skeletons/skeleton-card-tile.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import "@home-assistant/webawesome/dist/components/skeleton/skeleton";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-skeleton-tile")
|
||||
export class HaSkeletonTile extends LitElement {
|
||||
protected render() {
|
||||
return html`
|
||||
<div class="tile">
|
||||
<wa-skeleton class="icon" effect="sheen"></wa-skeleton>
|
||||
<div class="info">
|
||||
<wa-skeleton class="primary" effect="sheen"></wa-skeleton>
|
||||
<wa-skeleton class="secondary" effect="sheen"></wa-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
wa-skeleton {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
--color: var(--ha-skeleton-color, var(--secondary-background-color));
|
||||
--sheen-color: var(
|
||||
--ha-skeleton-sheen-color,
|
||||
color-mix(
|
||||
in srgb,
|
||||
var(--ha-skeleton-color, var(--secondary-background-color)) 70%,
|
||||
white
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
wa-skeleton::part(indicator) {
|
||||
border-radius: var(--ha-border-radius-sm);
|
||||
}
|
||||
|
||||
.tile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-4);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.icon::part(indicator) {
|
||||
border-radius: var(--ha-border-radius-circle);
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.primary {
|
||||
height: 18px;
|
||||
width: min(68%, 240px);
|
||||
}
|
||||
|
||||
.secondary {
|
||||
height: 14px;
|
||||
width: min(22%, 84px);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-skeleton-tile": HaSkeletonTile;
|
||||
}
|
||||
}
|
||||
97
src/panels/lovelace/cards/hui-skeleton-card.ts
Normal file
97
src/panels/lovelace/cards/hui-skeleton-card.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/skeletons/skeleton-card-tile";
|
||||
import type { HaFormSchema } from "../../../components/ha-form/types";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type {
|
||||
LovelaceCard,
|
||||
LovelaceConfigForm,
|
||||
LovelaceGridOptions,
|
||||
} from "../types";
|
||||
import type { SkeletonCardConfig } from "./types";
|
||||
|
||||
const SCHEMA = [
|
||||
{
|
||||
name: "mode",
|
||||
selector: {
|
||||
select: {
|
||||
mode: "dropdown",
|
||||
options: ["tile"],
|
||||
},
|
||||
},
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[];
|
||||
|
||||
@customElement("hui-skeleton-card")
|
||||
export class HuiSkeletonCard extends LitElement implements LovelaceCard {
|
||||
public static getConfigForm(): LovelaceConfigForm {
|
||||
return {
|
||||
schema: [...SCHEMA],
|
||||
computeLabel: (schema, localize) =>
|
||||
schema.name === "mode"
|
||||
? localize("ui.panel.lovelace.editor.card.skeleton.mode")
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
private _config?: SkeletonCardConfig;
|
||||
|
||||
public getCardSize(): number {
|
||||
switch (this._config?.mode ?? "tile") {
|
||||
case "tile":
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public getGridOptions(): LovelaceGridOptions {
|
||||
switch (this._config?.mode ?? "tile") {
|
||||
case "tile":
|
||||
default:
|
||||
return {
|
||||
columns: 6,
|
||||
rows: 1,
|
||||
min_columns: 6,
|
||||
min_rows: 1,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public setConfig(config: SkeletonCardConfig): void {
|
||||
this._config = {
|
||||
mode: "tile",
|
||||
...config,
|
||||
};
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-card>
|
||||
<ha-skeleton-tile></ha-skeleton-tile>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
height: 100%;
|
||||
padding: var(--ha-space-2);
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-skeleton-card": HuiSkeletonCard;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
||||
import { stateActive } from "../../../common/entity/state_active";
|
||||
import { stateColorCss } from "../../../common/entity/state_color";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/skeletons/skeleton-card-tile";
|
||||
import "../../../components/ha-state-icon";
|
||||
import "../../../components/tile/ha-tile-badge";
|
||||
import "../../../components/tile/ha-tile-container";
|
||||
@@ -27,7 +28,6 @@ import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-ent
|
||||
import { findEntities } from "../common/find-entities";
|
||||
import { handleAction } from "../common/handle-action";
|
||||
import { hasAction } from "../common/has-action";
|
||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
|
||||
import type {
|
||||
LovelaceCard,
|
||||
LovelaceCardEditor,
|
||||
@@ -237,37 +237,44 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
return features;
|
||||
});
|
||||
|
||||
private _shouldRenderSkeleton(stateObj?: HassEntity) {
|
||||
return !this.hass || !stateObj;
|
||||
}
|
||||
|
||||
private _renderSkeleton() {
|
||||
return html`
|
||||
<ha-card class="skeleton">
|
||||
<ha-skeleton-tile></ha-skeleton-tile>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._config || !this.hass) {
|
||||
if (!this._config) {
|
||||
return nothing;
|
||||
}
|
||||
const entityId = this._config.entity;
|
||||
const stateObj = entityId ? this.hass.states[entityId] : undefined;
|
||||
|
||||
if (!stateObj) {
|
||||
return html`
|
||||
<hui-warning .hass=${this.hass}>
|
||||
${createEntityNotFoundWarning(this.hass, this._config.entity)}
|
||||
</hui-warning>
|
||||
`;
|
||||
const entityId = this._config.entity;
|
||||
const stateObj = entityId ? this.hass?.states[entityId] : undefined;
|
||||
|
||||
if (this._shouldRenderSkeleton(stateObj)) {
|
||||
return this._renderSkeleton();
|
||||
}
|
||||
|
||||
const name = computeLovelaceEntityName(
|
||||
this.hass,
|
||||
stateObj,
|
||||
this._config.name
|
||||
);
|
||||
const hass = this.hass!;
|
||||
|
||||
const active = stateActive(stateObj);
|
||||
const color = this._computeStateColor(stateObj, this._config.color);
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
const name = computeLovelaceEntityName(hass, stateObj!, this._config.name);
|
||||
|
||||
const active = stateActive(stateObj!);
|
||||
const color = this._computeStateColor(stateObj!, this._config.color);
|
||||
const domain = computeDomain(stateObj!.entity_id);
|
||||
|
||||
const stateDisplay = this._config.hide_state
|
||||
? nothing
|
||||
: html`
|
||||
<state-display
|
||||
.stateObj=${stateObj}
|
||||
.hass=${this.hass}
|
||||
.hass=${hass}
|
||||
.content=${this._config.state_content}
|
||||
>
|
||||
</state-display>
|
||||
@@ -278,7 +285,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
};
|
||||
|
||||
const imageUrl = this._config.show_entity_picture
|
||||
? this._getImageUrl(stateObj)
|
||||
? this._getImageUrl(stateObj!)
|
||||
: undefined;
|
||||
|
||||
const featurePosition = this._featurePosition(this._config);
|
||||
@@ -308,7 +315,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
.interactive=${this._hasIconAction}
|
||||
.imageUrl=${imageUrl}
|
||||
data-domain=${ifDefined(domain)}
|
||||
data-state=${ifDefined(stateObj?.state)}
|
||||
data-state=${ifDefined(stateObj!.state)}
|
||||
class=${classMap({ image: hasImage })}
|
||||
>
|
||||
${hasImage
|
||||
@@ -318,10 +325,10 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
slot="icon"
|
||||
.icon=${this._config.icon}
|
||||
.stateObj=${stateObj}
|
||||
.hass=${this.hass}
|
||||
.hass=${hass}
|
||||
></ha-state-icon>
|
||||
`}
|
||||
${renderTileBadge(stateObj, this.hass)}
|
||||
${renderTileBadge(stateObj!, hass)}
|
||||
</ha-tile-icon>
|
||||
<ha-tile-info slot="info">
|
||||
<span slot="primary" class="primary">${name}</span>
|
||||
@@ -333,7 +340,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
? html`
|
||||
<hui-card-features
|
||||
slot="features"
|
||||
.hass=${this.hass}
|
||||
.hass=${hass}
|
||||
.context=${this._featureContext}
|
||||
.color=${this._config.color}
|
||||
.features=${features}
|
||||
@@ -354,6 +361,10 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
||||
ha-card.active {
|
||||
--tile-color: var(--state-icon-color);
|
||||
}
|
||||
ha-card.skeleton {
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
}
|
||||
ha-tile-badge {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
|
||||
@@ -600,6 +600,11 @@ export interface SensorCardConfig extends LovelaceCardConfig {
|
||||
};
|
||||
}
|
||||
|
||||
export interface SkeletonCardConfig extends LovelaceCardConfig {
|
||||
type: "skeleton";
|
||||
mode?: "tile";
|
||||
}
|
||||
|
||||
export interface TodoListCardConfig extends LovelaceCardConfig {
|
||||
title?: string;
|
||||
theme?: string;
|
||||
|
||||
@@ -95,6 +95,7 @@ const LAZY_LOAD_TYPES = {
|
||||
picture: () => import("../cards/hui-picture-card"),
|
||||
"plant-status": () => import("../cards/hui-plant-status-card"),
|
||||
"recovery-mode": () => import("../cards/hui-recovery-mode-card"),
|
||||
skeleton: () => import("../cards/hui-skeleton-card"),
|
||||
"toggle-group": () => import("../cards/hui-toggle-group-card"),
|
||||
"todo-list": () => import("../cards/hui-todo-list-card"),
|
||||
"shopping-list": () => import("../cards/hui-shopping-list-card"),
|
||||
|
||||
@@ -9179,6 +9179,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"skeleton": {
|
||||
"name": "Skeleton",
|
||||
"description": "This card displays a placeholder skeleton layout.",
|
||||
"mode": "Mode"
|
||||
},
|
||||
"media-control": {
|
||||
"name": "Media control",
|
||||
"description": "This card is used to display media player entities on an interface with easy to use controls."
|
||||
|
||||
Reference in New Issue
Block a user