mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-17 23:54:28 +01:00
Markdown card: add support of actions (#28951)
* add actions * add actions * add actions to MarkdownCardConfig * add "actions_warning" for Markdown * Add a "warning" label * process a default handler if none action is defined * check for config=undefined * fix attributes * prettier * Update src/translations/en.json Co-authored-by: Norbert Rittel <norbert@rittel.de> * add ripple * Fix interactive and add missing import for ripple * Fix translation --------- Co-authored-by: Norbert Rittel <norbert@rittel.de> Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
This commit is contained in:
@@ -3,18 +3,24 @@ import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import hash from "object-hash";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-markdown";
|
||||
import "../../../components/ha-ripple";
|
||||
import type { RenderTemplateResult } from "../../../data/ws-templates";
|
||||
import { subscribeRenderTemplate } from "../../../data/ws-templates";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { CacheManager } from "../../../util/cache-manager";
|
||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import type { MarkdownCardConfig } from "./types";
|
||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
import { handleAction } from "../common/handle-action";
|
||||
import { hasAction } from "../common/has-action";
|
||||
|
||||
const templateCache = new CacheManager<RenderTemplateResult>(1000);
|
||||
|
||||
@@ -56,6 +62,14 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
: this._config.card_size;
|
||||
}
|
||||
|
||||
private get _interactive() {
|
||||
return (
|
||||
hasAction(this._config?.tap_action) ||
|
||||
hasAction(this._config?.hold_action) ||
|
||||
hasAction(this._config?.double_tap_action)
|
||||
);
|
||||
}
|
||||
|
||||
public setConfig(config: MarkdownCardConfig): void {
|
||||
if (!config.content) {
|
||||
throw new Error("Content required");
|
||||
@@ -104,7 +118,13 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
if (!this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const safeHandleAction = this._interactive ? this._handleAction : undefined;
|
||||
const safeActionHandler = this._interactive
|
||||
? actionHandler({
|
||||
hasHold: hasAction(this._config.hold_action),
|
||||
hasDoubleClick: hasAction(this._config.double_tap_action),
|
||||
})
|
||||
: undefined;
|
||||
return html`
|
||||
${this._error
|
||||
? html`
|
||||
@@ -122,8 +142,22 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
class=${classMap({
|
||||
"with-header": !!this._config.title,
|
||||
"text-only": this._config.text_only ?? false,
|
||||
action: this._interactive,
|
||||
})}
|
||||
tabindex=${ifDefined(this._interactive ? "0" : undefined)}
|
||||
role=${ifDefined(this._interactive ? "button" : undefined)}
|
||||
@action=${safeHandleAction}
|
||||
.actionHandler=${safeActionHandler}
|
||||
>
|
||||
${this._interactive
|
||||
? html`<div
|
||||
class="background"
|
||||
@focus=${this._handleFocus}
|
||||
@blur=${this._handleBlur}
|
||||
>
|
||||
<ha-ripple></ha-ripple>
|
||||
</div>`
|
||||
: nothing}
|
||||
<ha-markdown
|
||||
cache
|
||||
breaks
|
||||
@@ -228,11 +262,37 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
this._errorLevel = undefined;
|
||||
}
|
||||
|
||||
private _handleAction(ev: ActionHandlerEvent) {
|
||||
handleAction(this, this.hass!, this._config!, ev.detail.action!);
|
||||
}
|
||||
|
||||
private _handleFocus(ev: FocusEvent) {
|
||||
if ((ev.target as HTMLElement).matches(":focus-visible")) {
|
||||
this.setAttribute("focused", "");
|
||||
}
|
||||
}
|
||||
|
||||
private _handleBlur() {
|
||||
this.removeAttribute("focused");
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
--ha-ripple-hover-opacity: 0.04;
|
||||
--ha-ripple-pressed-opacity: 0.12;
|
||||
}
|
||||
ha-card {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
ha-alert {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -252,6 +312,9 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
.text-only ha-markdown {
|
||||
padding: 2px 4px;
|
||||
}
|
||||
.action {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -426,6 +426,9 @@ export interface MarkdownCardConfig extends LovelaceCardConfig {
|
||||
entity_ids?: string | string[];
|
||||
theme?: string;
|
||||
show_empty?: boolean;
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
double_tap_action?: ActionConfig;
|
||||
}
|
||||
|
||||
export interface ClockCardConfig extends LovelaceCardConfig {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { mdiGestureTap } from "@mdi/js";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -12,7 +13,14 @@ import type {
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { MarkdownCardConfig } from "../../cards/types";
|
||||
import type { LovelaceCardEditor } from "../../types";
|
||||
import { actionConfigStruct } from "../structs/action-struct";
|
||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||
import {
|
||||
type UiAction,
|
||||
supportedActions,
|
||||
} from "../../components/hui-action-editor";
|
||||
|
||||
const TAP_ACTIONS: UiAction[] = ["navigate", "url", "perform-action", "none"];
|
||||
|
||||
const cardConfigStruct = assign(
|
||||
baseLovelaceCardConfig,
|
||||
@@ -20,6 +28,11 @@ const cardConfigStruct = assign(
|
||||
text_only: optional(boolean()),
|
||||
title: optional(string()),
|
||||
content: string(),
|
||||
tap_action: optional(supportedActions(actionConfigStruct, TAP_ACTIONS)),
|
||||
hold_action: optional(supportedActions(actionConfigStruct, TAP_ACTIONS)),
|
||||
double_tap_action: optional(
|
||||
supportedActions(actionConfigStruct, TAP_ACTIONS)
|
||||
),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -68,6 +81,43 @@ export class HuiMarkdownCardEditor
|
||||
required: true,
|
||||
selector: { template: { preview: false } },
|
||||
},
|
||||
{
|
||||
name: "interactions",
|
||||
type: "expandable",
|
||||
flatten: true,
|
||||
iconPath: mdiGestureTap,
|
||||
schema: [
|
||||
{
|
||||
name: "actions_warning",
|
||||
type: "constant",
|
||||
},
|
||||
{
|
||||
name: "tap_action",
|
||||
selector: {
|
||||
ui_action: {
|
||||
actions: TAP_ACTIONS,
|
||||
default_action: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "",
|
||||
type: "optional_actions",
|
||||
flatten: true,
|
||||
schema: (["hold_action", "double_tap_action"] as const).map(
|
||||
(action) => ({
|
||||
name: action,
|
||||
selector: {
|
||||
ui_action: {
|
||||
actions: TAP_ACTIONS,
|
||||
default_action: "none" as const,
|
||||
},
|
||||
},
|
||||
})
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
] as const satisfies HaFormSchema[]
|
||||
);
|
||||
|
||||
@@ -119,6 +169,20 @@ export class HuiMarkdownCardEditor
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.markdown.${schema.name}`
|
||||
);
|
||||
case "interactions":
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.generic.interactions"
|
||||
);
|
||||
case "actions_warning":
|
||||
return this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.card.markdown.actions_warning"
|
||||
);
|
||||
case "tap_action":
|
||||
case "hold_action":
|
||||
case "double_tap_action":
|
||||
return `${this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
)} (${this.hass!.localize("ui.panel.lovelace.editor.card.config.optional")})`;
|
||||
default:
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
|
||||
@@ -9313,6 +9313,7 @@
|
||||
"card": "Card",
|
||||
"text-only": "Text only"
|
||||
},
|
||||
"actions_warning": "Adding an action will disable links in the content.",
|
||||
"description": "This card is used to render Markdown."
|
||||
},
|
||||
"clock": {
|
||||
|
||||
Reference in New Issue
Block a user