Migrate ha-tooltip to webawesome (#26540)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
Simon Lamon
2025-09-12 13:09:24 +02:00
committed by GitHub
parent 0d8d18617c
commit dcbc8b627f
34 changed files with 451 additions and 449 deletions

View File

@@ -6,21 +6,23 @@ A tooltip's target is its _first child element_, so you should only wrap one ele
Tooltips use `display: contents` so they won't interfere with how elements are positioned in a flex or grid layout.
<ha-tooltip content="This is a tooltip">
<ha-button>Hover Me</ha-button>
<ha-button id="hover">Hover Me</ha-button>
<ha-tooltip for="hover">
This is a tooltip
</ha-tooltip>
```
<ha-tooltip content="This is a tooltip">
<ha-button>Hover Me</ha-button>
<ha-button id="hover">Hover Me</ha-button>
<ha-tooltip for="hover">
This is a tooltip
</ha-tooltip>
```
## Documentation
This element is based on shoelace `sl-tooltip` it only sets some css tokens and has a custom show/hide animation.
This element is based on webawesome `wa-tooltip` it only sets some css tokens and has a custom show/hide animation.
<a href="https://shoelace.style/components/tooltip" target="_blank" rel="noopener noreferrer">Shoelace documentation</a>
<a href="https://webawesome.com/docs/components/tooltip/" target="_blank" rel="noopener noreferrer">Webawesome documentation</a>
### HA style tokens
@@ -28,7 +30,7 @@ In your theme settings use this without the prefixed `--`.
- `--ha-tooltip-border-radius` (Default: 4px)
- `--ha-tooltip-arrow-size` (Default: 8px)
- `--sl-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
- `--wa-tooltip-font-family` (Default: `var(--ha-font-family-body)`)
- `--ha-tooltip-font-size` (Default: `var(--ha-font-size-s)`)
- `--sl-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
- `--sl-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)
- `--wa-tooltip-font-weight` (Default: `var(--ha-font-weight-normal)`)
- `--wa-tooltip-line-height` (Default: `var(--ha-line-height-condensed)`)

View File

@@ -119,26 +119,27 @@ class HassioRepositoriesDialog extends LitElement {
<div>${repo.url}</div>
</div>
<ha-tooltip
.for="icon-button-${repo.slug}"
class="delete"
slot="end"
.content=${this._dialogParams!.supervisor.localize(
>
${this._dialogParams!.supervisor.localize(
usedRepositories.includes(repo.slug)
? "dialog.repositories.used"
: "dialog.repositories.remove"
)}
>
<div>
<ha-icon-button
.disabled=${usedRepositories.includes(repo.slug)}
.slug=${repo.slug}
.path=${usedRepositories.includes(repo.slug)
? mdiDeleteOff
: mdiDelete}
@click=${this._removeRepository}
>
</ha-icon-button>
</div>
</ha-tooltip>
<div .id="icon-button-${repo.slug}">
<ha-icon-button
.disabled=${usedRepositories.includes(repo.slug)}
.slug=${repo.slug}
.path=${usedRepositories.includes(repo.slug)
? mdiDeleteOff
: mdiDelete}
@click=${this._removeRepository}
>
</ha-icon-button>
</div>
</ha-md-list-item>
`
)

View File

@@ -51,7 +51,7 @@
"@fullcalendar/list": "6.1.19",
"@fullcalendar/luxon3": "6.1.19",
"@fullcalendar/timegrid": "6.1.19",
"@home-assistant/webawesome": "3.0.0-beta.4.ha.2",
"@home-assistant/webawesome": "3.0.0-beta.4.ha.3",
"@lezer/highlight": "1.2.1",
"@lit-labs/motion": "1.0.9",
"@lit-labs/observers": "2.0.6",
@@ -84,7 +84,6 @@
"@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47",
"@replit/codemirror-indentation-markers": "6.5.3",
"@shoelace-style/shoelace": "2.20.1",
"@swc/helpers": "0.5.17",
"@thomasloven/round-slider": "0.6.0",
"@tsparticles/engine": "3.9.1",

View File

@@ -12,9 +12,8 @@ class HaDataTableIcon extends LitElement {
protected render(): TemplateResult {
return html`
<ha-tooltip .content=${this.tooltip}>
<ha-svg-icon .path=${this.path}></ha-svg-icon>
</ha-tooltip>
<ha-tooltip for="svg-icon">${this.tooltip}</ha-tooltip>
<ha-svg-icon id="svg-icon" .path=${this.path}></ha-svg-icon>
`;
}

View File

@@ -36,39 +36,38 @@ class StateInfo extends LitElement {
</div>
${this.inDialog
? html`<div class="time-ago">
<ha-tooltip>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
<div slot="content">
<div class="row">
<span class="column-name">
${this.hass.localize(
"ui.dialogs.more_info_control.last_changed"
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
</div>
<div class="row">
<span>
${this.hass.localize(
"ui.dialogs.more_info_control.last_updated"
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_updated}
capitalize
></ha-relative-time>
</div>
<ha-tooltip for="relative-time">
<div class="row">
<span class="column-name">
${this.hass.localize(
"ui.dialogs.more_info_control.last_changed"
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
</div>
<div class="row">
<span>
${this.hass.localize(
"ui.dialogs.more_info_control.last_updated"
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_updated}
capitalize
></ha-relative-time>
</div>
</ha-tooltip>
<ha-relative-time
id="relative-time"
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
</div>`
: html`<div class="extra-info"><slot></slot></div>`}
</div>`;

View File

@@ -67,20 +67,19 @@ export class HaAnalytics extends LitElement {
)}
</span>
<span>
<ha-tooltip
content=${this.localize(
`ui.panel.${this.translationKeyPanel}.analytics.need_base_enabled`
)}
placement="right"
<ha-switch
.id="switch-${preference}"
@change=${this._handleRowClick}
.checked=${this.analytics?.preferences[preference]}
.preference=${preference}
name=${preference}
?disabled=${baseEnabled}
>
<ha-switch
@change=${this._handleRowClick}
.checked=${this.analytics?.preferences[preference]}
.preference=${preference}
name=${preference}
>
</ha-switch>
</ha-switch>
<ha-tooltip .for="switch-${preference}" placement="right">
${this.localize(
`ui.panel.${this.translationKeyPanel}.analytics.need_base_enabled`
)}
</ha-tooltip>
</span>
</ha-settings-row>

View File

@@ -25,8 +25,9 @@ export class HaHelpTooltip extends LitElement {
protected render(): TemplateResult {
return html`
<ha-tooltip .placement=${this.position} .content=${this.label}>
<ha-svg-icon .path=${mdiHelpCircle}></ha-svg-icon>
<ha-svg-icon id="svg-icon" .path=${mdiHelpCircle}></ha-svg-icon>
<ha-tooltip for="svg-icon" .placement=${this.position}>
${this.label}
</ha-tooltip>
`;
}

View File

@@ -74,16 +74,16 @@ export class HaIconOverflowMenu extends LitElement {
: item.divider
? html`<div role="separator"></div>`
: html`<ha-tooltip
.disabled=${!item.tooltip}
.content=${item.tooltip ?? ""}
>
<ha-icon-button
.disabled=${!item.tooltip}
.for="icon-button-${item.label}"
>${item.tooltip ?? ""} </ha-tooltip
><ha-icon-button
.id="icon-button-${item.label}"
@click=${item.action}
.label=${item.label}
.path=${item.path}
?disabled=${item.disabled}
></ha-icon-button>
</ha-tooltip>`
></ha-icon-button> `
)}
`}
`;

View File

@@ -343,40 +343,36 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
${type === "entity_id"
? ""
: html`<span role="gridcell">
<ha-tooltip
.content=${this.hass.localize(
<ha-tooltip .for="expand-${id}"
>${this.hass.localize(
`ui.components.target-picker.expand_${type}`
)}
>
<ha-icon-button
class="expand-btn mdc-chip__icon mdc-chip__icon--trailing"
.label=${this.hass.localize(
"ui.components.target-picker.expand"
)}
.path=${mdiUnfoldMoreVertical}
hide-title
.id=${id}
.type=${type}
@click=${this._handleExpand}
></ha-icon-button>
</ha-tooltip>
<ha-icon-button
class="expand-btn mdc-chip__icon mdc-chip__icon--trailing"
.label=${this.hass.localize(
"ui.components.target-picker.expand"
)}
.path=${mdiUnfoldMoreVertical}
hide-title
.id="expand-${id}"
.type=${type}
@click=${this._handleExpand}
></ha-icon-button>
</span>`}
<span role="gridcell">
<ha-tooltip
.content=${this.hass.localize(
`ui.components.target-picker.remove_${type}`
)}
>
<ha-icon-button
class="mdc-chip__icon mdc-chip__icon--trailing"
.label=${this.hass.localize("ui.components.target-picker.remove")}
.path=${mdiClose}
hide-title
.id=${id}
.type=${type}
@click=${this._handleRemove}
></ha-icon-button>
<ha-tooltip .for="remove-${id}">
${this.hass.localize(`ui.components.target-picker.remove_${type}`)}
</ha-tooltip>
<ha-icon-button
class="mdc-chip__icon mdc-chip__icon--trailing"
.label=${this.hass.localize("ui.components.target-picker.remove")}
.path=${mdiClose}
hide-title
.id="remove-${id}"
.type=${type}
@click=${this._handleRemove}
></ha-icon-button>
</span>
</div>
`;

View File

@@ -1,50 +1,47 @@
import SlTooltip from "@shoelace-style/shoelace/dist/components/tooltip/tooltip.component";
import styles from "@shoelace-style/shoelace/dist/components/tooltip/tooltip.styles";
import Tooltip from "@home-assistant/webawesome/dist/components/tooltip/tooltip";
import { css } from "lit";
import { customElement } from "lit/decorators";
import { setDefaultAnimation } from "@shoelace-style/shoelace/dist/utilities/animation-registry";
setDefaultAnimation("tooltip.show", {
keyframes: [{ opacity: 0 }, { opacity: 1 }],
options: { duration: 150, easing: "ease" },
});
setDefaultAnimation("tooltip.hide", {
keyframes: [{ opacity: 1 }, { opacity: 0 }],
options: { duration: 400, easing: "ease" },
});
import type { CSSResultGroup } from "lit";
import { customElement, property } from "lit/decorators";
@customElement("ha-tooltip")
export class HaTooltip extends SlTooltip {
static override styles = [
styles,
css`
:host {
--sl-tooltip-background-color: var(--secondary-background-color);
--sl-tooltip-color: var(--primary-text-color);
--sl-tooltip-font-family: var(
--ha-tooltip-font-family,
var(--ha-font-family-body)
);
--sl-tooltip-font-size: var(
--ha-tooltip-font-size,
var(--ha-font-size-s)
);
--sl-tooltip-font-weight: var(
--ha-tooltip-font-weight,
var(--ha-font-weight-normal)
);
--sl-tooltip-line-height: var(
--ha-tooltip-line-height,
var(--ha-line-height-condensed)
);
--sl-tooltip-padding: 8px;
--sl-tooltip-border-radius: var(--ha-tooltip-border-radius, 4px);
--sl-tooltip-arrow-size: var(--ha-tooltip-arrow-size, 8px);
--sl-z-index-tooltip: var(--ha-tooltip-z-index, 1000);
}
`,
];
export class HaTooltip extends Tooltip {
/** The amount of time to wait before showing the tooltip when the user mouses in. */
@property({ attribute: "show-delay", type: Number }) showDelay = 150;
/** The amount of time to wait before hiding the tooltip when the user mouses out.. */
@property({ attribute: "hide-delay", type: Number }) hideDelay = 400;
static get styles(): CSSResultGroup {
return [
Tooltip.styles,
css`
:host {
--wa-tooltip-background-color: var(--secondary-background-color);
--wa-tooltip-color: var(--primary-text-color);
--wa-tooltip-font-family: var(
--ha-tooltip-font-family,
var(--ha-font-family-body)
);
--wa-tooltip-font-size: var(
--ha-tooltip-font-size,
var(--ha-font-size-s)
);
--wa-tooltip-font-weight: var(
--ha-tooltip-font-weight,
var(--ha-font-weight-normal)
);
--wa-tooltip-line-height: var(
--ha-tooltip-line-height,
var(--ha-line-height-condensed)
);
--wa-tooltip-padding: 8px;
--wa-tooltip-border-radius: var(--ha-tooltip-border-radius, 4px);
--wa-tooltip-arrow-size: var(--ha-tooltip-arrow-size, 8px);
--wa-z-index-tooltip: var(--ha-tooltip-z-index, 1000);
}
`,
];
}
}
declare global {

View File

@@ -642,9 +642,10 @@ export class HaMediaPlayerBrowse extends LitElement {
`
: ""}
</div>
<ha-tooltip distance="-4" .content=${child.title}>
<div class="title">${child.title}</div>
<ha-tooltip .for="grid-${child.title}" distance="-4">
${child.title}
</ha-tooltip>
<div .id="grid-${child.title}" class="title">${child.title}</div>
</ha-card>
</div>
`;

View File

@@ -166,37 +166,36 @@ class MoreInfoWeather extends LitElement {
${this.hass.formatEntityState(this.stateObj)}
</div>
<div class="time-ago">
<ha-tooltip>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
<div slot="content">
<div class="row">
<span class="column-name">
${this.hass.localize(
"ui.dialogs.more_info_control.last_changed"
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
</div>
<div class="row">
<span>
${this.hass.localize(
"ui.dialogs.more_info_control.last_updated"
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_updated}
capitalize
></ha-relative-time>
</div>
<ha-relative-time
id="relative-time"
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
<ha-tooltip for="relative-time">
<div class="row">
<span class="column-name">
${this.hass.localize(
"ui.dialogs.more_info_control.last_changed"
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_changed}
capitalize
></ha-relative-time>
</div>
<div class="row">
<span>
${this.hass.localize(
"ui.dialogs.more_info_control.last_updated"
)}:
</span>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.stateObj.last_updated}
capitalize
></ha-relative-time>
</div>
</ha-tooltip>
</div>

View File

@@ -28,15 +28,14 @@ export class HuiPersistentNotificationItem extends LitElement {
<div class="time">
<span>
<ha-tooltip
.content=${this._computeTooltip(this.hass, this.notification)}
placement="bottom"
>
<ha-relative-time
.hass=${this.hass}
.datetime=${this.notification.created_at}
capitalize
></ha-relative-time>
<ha-relative-time
id="relative-time"
.hass=${this.hass}
.datetime=${this.notification.created_at}
capitalize
></ha-relative-time>
<ha-tooltip for="relative-time" placement="bottom">
${this._computeTooltip(this.hass, this.notification)}
</ha-tooltip>
</span>
</div>

View File

@@ -51,6 +51,7 @@ import {
loadAreaRegistryDetailDialog,
showAreaRegistryDetailDialog,
} from "./show-dialog-area-registry-detail";
import { slugify } from "../../../common/string/slugify";
declare interface NameAndEntity<EntityType extends HassEntity> {
name: string;
@@ -549,11 +550,14 @@ class HaConfigAreaPage extends LitElement {
private _renderScene(name: string, entityState: SceneEntity) {
return html`<ha-tooltip
.distance=${-4}
.disabled=${!!entityState.attributes.id}
.content=${this.hass.localize("ui.panel.config.devices.cant_edit")}
>
.for="scene-${slugify(entityState.entity_id)}"
.distance=${-4}
.disabled=${!!entityState.attributes.id}
>
${this.hass.localize("ui.panel.config.devices.cant_edit")}
</ha-tooltip>
<a
.id="scene-${slugify(entityState.entity_id)}"
href=${ifDefined(
entityState.attributes.id
? `/config/scene/edit/${entityState.attributes.id}`
@@ -564,17 +568,12 @@ class HaConfigAreaPage extends LitElement {
<span>${name}</span>
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>
</ha-tooltip>`;
</a> `;
}
private _renderAutomation(name: string, entityState: AutomationEntity) {
return html`<ha-tooltip
.disabled=${!!entityState.attributes.id}
.distance=${-4}
.content=${this.hass.localize("ui.panel.config.devices.cant_edit")}
>
<a
return html`<a
id="automation-${slugify(entityState.entity_id)}"
href=${ifDefined(
entityState.attributes.id
? `/config/automation/edit/${encodeURIComponent(entityState.attributes.id)}`
@@ -586,7 +585,12 @@ class HaConfigAreaPage extends LitElement {
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</a>
</ha-tooltip>`;
<ha-tooltip
for="automation-${slugify(entityState.entity_id)}"
.disabled=${!!entityState.attributes.id}
.distance=${-4}
>${this.hass.localize("ui.panel.config.devices.cant_edit")}
</ha-tooltip>`;
}
private _renderScript(name: string, entityState: ScriptEntity) {

View File

@@ -258,14 +258,16 @@ export default class HaAutomationActionRow extends LitElement {
${type !== "condition" &&
(this.action as NonConditionAction).continue_on_error === true
? html`<ha-tooltip
slot="icons"
.content=${this.hass.localize(
"ui.panel.config.automation.editor.actions.continue_on_error"
)}
>
<ha-svg-icon .path=${mdiAlertCircleCheck}></ha-svg-icon>
</ha-tooltip>`
? html`<ha-svg-icon
id="svg-icon"
slot="icons"
.path=${mdiAlertCircleCheck}
></ha-svg-icon>
<ha-tooltip for="svg-icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.continue_on_error"
)}
</ha-tooltip>`
: nothing}
${!this.optionsInSidebar
? html`<ha-md-button-menu

View File

@@ -89,6 +89,7 @@ import {
loadDeviceRegistryDetailDialog,
showDeviceRegistryDetailDialog,
} from "./device-registry-detail/show-dialog-device-registry-detail";
import { slugify } from "../../../common/string/slugify";
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
stateName?: string | null;
@@ -555,16 +556,21 @@ export class HaConfigDevicePage extends LitElement {
</a>
`
: html`
<ha-list-item
.id="scene-${slugify(entityState.entity_id)}"
hasMeta
.scene=${entityState}
>
${computeStateName(entityState)}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
<ha-tooltip
.for="scene-${slugify(entityState.entity_id)}"
placement="left"
.content=${this.hass.localize(
>
${this.hass.localize(
"ui.panel.config.devices.cant_edit"
)}
>
<ha-list-item hasMeta .scene=${entityState}>
${computeStateName(entityState)}
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</ha-tooltip>
`;
})}

View File

@@ -671,13 +671,14 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
tabindex="0"
style="display:inline-block; position: relative;"
>
<ha-tooltip
placement="left"
.content=${this.hass.localize(
<ha-svg-icon
.id="svg-icon-${device.id}"
.path=${mdiCancel}
></ha-svg-icon>
<ha-tooltip .for="svg-icon-${device.id}" placement="left">
${this.hass.localize(
"ui.panel.config.entities.picker.status.disabled"
)}
>
<ha-svg-icon .path=${mdiCancel}></ha-svg-icon>
</ha-tooltip>
</div>
`

View File

@@ -114,6 +114,7 @@ import { isHelperDomain } from "../helpers/const";
import "../integrations/ha-integration-overflow-menu";
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import { slugify } from "../../../common/string/slugify";
export interface StateEntity
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
@@ -392,9 +393,27 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
tabindex="0"
style="display:inline-block; position: relative;"
>
<ha-svg-icon
.id="status-icon-${slugify(entry.entity_id)}"
style=${styleMap({
color: entry.unavailable ? "var(--error-color)" : "",
})}
.path=${entry.restored
? mdiRestoreAlert
: entry.unavailable
? mdiAlertCircle
: entry.disabled_by
? mdiCancel
: entry.hidden_by
? mdiEyeOff
: mdiPencilOff}
></ha-svg-icon>
<ha-tooltip
.for="status-icon-${slugify(entry.entity_id)}"
placement="left"
.content=${entry.restored
>
${entry.restored
? this.hass.localize(
"ui.panel.config.entities.picker.status.not_provided"
)
@@ -413,21 +432,6 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
: this.hass.localize(
"ui.panel.config.entities.picker.status.unmanageable"
)}
>
<ha-svg-icon
style=${styleMap({
color: entry.unavailable ? "var(--error-color)" : "",
})}
.path=${entry.restored
? mdiRestoreAlert
: entry.unavailable
? mdiAlertCircle
: entry.disabled_by
? mdiCancel
: entry.hidden_by
? mdiEyeOff
: mdiPencilOff}
></ha-svg-icon>
</ha-tooltip>
</div>
`

View File

@@ -236,17 +236,18 @@ export class DialogHelperDetail extends LitElement {
<span class="item-text"> ${label} </span>
${isLoaded
? html`<ha-icon-next slot="meta"></ha-icon-next>`
: html`<ha-tooltip
hoist
slot="meta"
.content=${this.hass.localize(
"ui.dialogs.helper_settings.platform_not_loaded",
{ platform: domain }
)}
@click=${stopPropagation}
>
<ha-svg-icon path=${mdiAlertOutline}></ha-svg-icon>
</ha-tooltip>`}
: html` <ha-svg-icon
slot="meta"
.id="icon-${domain}"
path=${mdiAlertOutline}
@click=${stopPropagation}
></ha-svg-icon>
<ha-tooltip .for="icon-${domain}">
${this.hass.localize(
"ui.dialogs.helper_settings.platform_not_loaded",
{ platform: domain }
)}
</ha-tooltip>`}
</ha-list-item>
`;
})}

View File

@@ -110,6 +110,7 @@ import { renderConfigEntryError } from "../integrations/ha-config-integration-pa
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import { isHelperDomain } from "./const";
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
import { slugify } from "../../../common/string/slugify";
interface HelperItem {
id: string;
@@ -361,13 +362,16 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
tabindex="0"
style="display:inline-block; position: relative;"
>
<ha-svg-icon
.id="icon-edit-${slugify(helper.entity_id)}"
.path=${mdiPencilOff}
></ha-svg-icon>
<ha-tooltip
.for="icon-edit-${slugify(helper.entity_id)}"
placement="left"
.content=${this.hass.localize(
>${this.hass.localize(
"ui.panel.config.entities.picker.status.unmanageable"
)}
>
<ha-svg-icon .path=${mdiPencilOff}></ha-svg-icon>
</ha-tooltip>
</div>
`

View File

@@ -159,29 +159,32 @@ export class HaIntegrationCard extends LitElement {
? "overwrites"
: "custom"}"
>
<ha-svg-icon
id="icon-custom"
.path=${mdiPackageVariant}
></ha-svg-icon>
<ha-tooltip
hoist
for="icon-custom"
.placement=${computeRTL(this.hass) ? "right" : "left"}
.content=${this.hass.localize(
>
${this.hass.localize(
this.manifest.overwrites_built_in
? "ui.panel.config.integrations.config_entry.custom_overwrites_core"
: "ui.panel.config.integrations.config_entry.custom_integration"
)}
>
<ha-svg-icon .path=${mdiPackageVariant}></ha-svg-icon>
</ha-tooltip>
</span>`
: nothing}
${this.manifest && this.manifest.iot_class?.startsWith("cloud_")
? html`<div class="icon cloud">
<ha-svg-icon id="icon-cloud" .path=${mdiWeb}></ha-svg-icon>
<ha-tooltip
hoist
for="icon-cloud"
.placement=${computeRTL(this.hass) ? "right" : "left"}
.content=${this.hass.localize(
>
${this.hass.localize(
"ui.panel.config.integrations.config_entry.depends_on_cloud"
)}
>
<ha-svg-icon .path=${mdiWeb}></ha-svg-icon>
</ha-tooltip>
</div>`
: nothing}
@@ -189,15 +192,18 @@ export class HaIntegrationCard extends LitElement {
!this.manifest?.config_flow &&
!this.items.every((itm) => itm.source === "system")
? html`<div class="icon yaml">
<ha-svg-icon
id="icon-yaml"
.path=${mdiFileCodeOutline}
></ha-svg-icon>
<ha-tooltip
hoist
for="icon-yaml"
.placement=${computeRTL(this.hass) ? "right" : "left"}
.content=${this.hass.localize(
>
${this.hass.localize(
"ui.panel.config.integrations.config_entry.no_config_flow"
)}
>
<ha-svg-icon .path=${mdiFileCodeOutline}></ha-svg-icon
></ha-tooltip>
</ha-tooltip>
</div>`
: nothing}
</div>

View File

@@ -74,45 +74,45 @@ export class HaIntegrationListItem extends ListItemBase {
}
return html`<span class="mdc-deprecated-list-item__meta material-icons">
${this.integration.cloud
? html`<ha-tooltip
placement="left"
.content=${this.hass.localize(
"ui.panel.config.integrations.config_entry.depends_on_cloud"
)}
><ha-svg-icon .path=${mdiWeb}></ha-svg-icon
></ha-tooltip>`
? html` <ha-svg-icon id="icon-cloud" .path=${mdiWeb}></ha-svg-icon>
<ha-tooltip for="icon-cloud" placement="left"
>${this.hass.localize(
"ui.panel.config.integrations.config_entry.depends_on_cloud"
)}
</ha-tooltip>`
: nothing}
${!this.integration.is_built_in
? html`<span
class=${this.integration.overwrites_built_in
? "overwrites"
: "custom"}
><ha-tooltip
placement="left"
.content=${this.hass.localize(
>
<ha-svg-icon
id="icon-custom"
.path=${mdiPackageVariant}
></ha-svg-icon>
<ha-tooltip for="icon-custom" placement="left"
>${this.hass.localize(
this.integration.overwrites_built_in
? "ui.panel.config.integrations.config_entry.custom_overwrites_core"
: "ui.panel.config.integrations.config_entry.custom_integration"
)}
><ha-svg-icon
.path=${mdiPackageVariant}
></ha-svg-icon></ha-tooltip
></span>`
)}</ha-tooltip
></span
>`
: nothing}
${!this.integration.config_flow &&
!this.integration.integrations &&
!this.integration.iot_standards
? html`<ha-tooltip
placement="left"
.content=${this.hass.localize(
"ui.panel.config.integrations.config_entry.yaml_only"
)}
>
<ha-svg-icon
? html` <ha-svg-icon
id="icon-yaml"
.path=${mdiFileCodeOutline}
class="open-in-new"
></ha-svg-icon>
</ha-tooltip>`
<ha-tooltip for="icon-yaml" placement="left">
${this.hass.localize(
"ui.panel.config.integrations.config_entry.yaml_only"
)}
</ha-tooltip>`
: html`<ha-icon-next></ha-icon-next>`}
</span>`;
}

View File

@@ -307,14 +307,18 @@ class DialogZHAReconfigureDevice extends LitElement {
`
: html`
<span class="stage">
<ha-svg-icon
.id="svg-icon-${clusterStatus
.cluster.name}"
.path=${mdiCloseCircle}
class="failed"
></ha-svg-icon>
<ha-tooltip
.for="svg-icon-${clusterStatus
.cluster.name}"
placement="top"
.content=${attribute.status}
>
<ha-svg-icon
.path=${mdiCloseCircle}
class="failed"
></ha-svg-icon>
${attribute.status}
</ha-tooltip>
</span>
`}

View File

@@ -156,16 +156,18 @@ export class HaConfigLovelaceDashboards extends LitElement {
${dashboard.title}
${dashboard.default
? html`
<ha-svg-icon
.id="default-icon-${dashboard.title}"
style="padding-left: 10px; padding-inline-start: 10px; padding-inline-end: initial; direction: var(--direction);"
.path=${mdiCheckCircleOutline}
></ha-svg-icon>
<ha-tooltip
.content=${this.hass.localize(
`ui.panel.config.lovelace.dashboards.default_dashboard`
)}
.for="default-icon-${dashboard.title}"
placement="right"
>
<ha-svg-icon
style="padding-left: 10px; padding-inline-start: 10px; padding-inline-end: initial; direction: var(--direction);"
.path=${mdiCheckCircleOutline}
></ha-svg-icon>
${this.hass.localize(
`ui.panel.config.lovelace.dashboards.default_dashboard`
)}
</ha-tooltip>
`
: nothing}

View File

@@ -106,6 +106,7 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor
import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail";
import { configSections } from "../ha-panel-config";
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
import { slugify } from "../../../common/string/slugify";
type SceneItem = SceneEntity & {
name: string;
@@ -318,16 +319,18 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
template: (scene) =>
!scene.attributes.id
? html`
<ha-svg-icon
.id="svg-icon-${slugify(scene.entity_id)}"
.path=${mdiPencilOff}
style="color: var(--secondary-text-color)"
></ha-svg-icon>
<ha-tooltip
.for="svg-icon-${slugify(scene.entity_id)}"
placement="left"
.content=${this.hass.localize(
>
${this.hass.localize(
"ui.panel.config.scene.picker.only_editable"
)}
>
<ha-svg-icon
.path=${mdiPencilOff}
style="color: var(--secondary-text-color)"
></ha-svg-icon>
</ha-tooltip>
`
: nothing,

View File

@@ -25,48 +25,47 @@ export class VoiceAssistantExposeAssistantIcon extends LitElement {
if (!this.assistant || !voiceAssistants[this.assistant]) return nothing;
return html`
<div class="container" id="container">
<img
class="logo"
style=${styleMap({
filter: this.manual ? "grayscale(100%)" : undefined,
})}
alt=${voiceAssistants[this.assistant].name}
src=${brandsUrl({
domain: voiceAssistants[this.assistant].domain,
type: "icon",
darkOptimized: this.hass.themes?.darkMode,
})}
crossorigin="anonymous"
referrerpolicy="no-referrer"
slot="prefix"
/>
${this.unsupported
? html`
<ha-svg-icon
.path=${mdiAlertCircle}
class="unsupported"
></ha-svg-icon>
`
: nothing}
</div>
<ha-tooltip
.disabled=${!this.unsupported && !this.manual}
for="container"
placement="left"
.disabled=${!this.unsupported && !this.manual}
>
<div class="container">
<img
class="logo"
style=${styleMap({
filter: this.manual ? "grayscale(100%)" : undefined,
})}
alt=${voiceAssistants[this.assistant].name}
src=${brandsUrl({
domain: voiceAssistants[this.assistant].domain,
type: "icon",
darkOptimized: this.hass.themes?.darkMode,
})}
crossorigin="anonymous"
referrerpolicy="no-referrer"
slot="prefix"
/>
${this.unsupported
? html`
<ha-svg-icon
.path=${mdiAlertCircle}
class="unsupported"
></ha-svg-icon>
`
: nothing}
</div>
<span slot="content">
${this.unsupported
? this.hass.localize(
"ui.panel.config.voice_assistants.expose.not_supported"
)
: ""}
${this.unsupported && this.manual ? html`<br />` : nothing}
${this.manual
? this.hass.localize(
"ui.panel.config.voice_assistants.expose.manually_configured"
)
: nothing}
</span>
${this.unsupported
? this.hass.localize(
"ui.panel.config.voice_assistants.expose.not_supported"
)
: ""}
${this.unsupported && this.manual ? html`<br />` : nothing}
${this.manual
? this.hass.localize(
"ui.panel.config.voice_assistants.expose.manually_configured"
)
: nothing}
</ha-tooltip>
`;
}

View File

@@ -607,34 +607,32 @@ export class VoiceAssistantsExpose extends LitElement {
>
`
: html`
<ha-tooltip
.content=${this.hass.localize(
<ha-icon-button
id="expose-button"
@click=${this._exposeSelected}
.path=${mdiPlusBoxMultiple}
.label=${this.hass.localize(
"ui.panel.config.voice_assistants.expose.expose"
)}
></ha-icon-button>
<ha-tooltip for="expose-button" placement="left">
${this.hass.localize(
"ui.panel.config.voice_assistants.expose.expose"
)}
placement="left"
>
<ha-icon-button
@click=${this._exposeSelected}
.path=${mdiPlusBoxMultiple}
.label=${this.hass.localize(
"ui.panel.config.voice_assistants.expose.expose"
)}
></ha-icon-button>
</ha-tooltip>
<ha-tooltip
content=${this.hass.localize(
<ha-tooltip for="unexpose-button" placement="left">
${this.hass.localize(
"ui.panel.config.voice_assistants.expose.unexpose"
)}
placement="left"
>
<ha-icon-button
@click=${this._unexposeSelected}
.path=${mdiCloseBoxMultiple}
.label=${this.hass.localize(
"ui.panel.config.voice_assistants.expose.unexpose"
)}
></ha-icon-button>
</ha-tooltip>
<ha-icon-button
id="unexpose-button"
@click=${this._unexposeSelected}
.path=${mdiCloseBoxMultiple}
.label=${this.hass.localize(
"ui.panel.config.voice_assistants.expose.unexpose"
)}
></ha-icon-button>
`}
</div>
`

View File

@@ -46,6 +46,7 @@ import "../ha-config-section";
import { configSections } from "../ha-panel-config";
import { showHomeZoneDetailDialog } from "./show-dialog-home-zone-detail";
import { showZoneDetailDialog } from "./show-dialog-zone-detail";
import { slugify } from "../../../common/string/slugify";
@customElement("ha-config-zone")
export class HaConfigZone extends SubscribeMixin(LitElement) {
@@ -200,17 +201,8 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
stateObject.entity_id === "zone.home" &&
!this._canEditCore
? nothing
: html`<ha-tooltip
slot="meta"
placement="left"
.content=${hass.localize(
"ui.panel.config.zone.configured_in_yaml"
)}
.disabled=${stateObject.entity_id === "zone.home"}
hoist
>
<ha-icon-button
.id=${!this.narrow ? stateObject.entity_id : ""}
: html`<ha-icon-button
.id="zone-${slugify(stateObject.entity_id)}"
.entityId=${stateObject.entity_id}
.noEdit=${stateObject.entity_id !== "zone.home" ||
!this._canEditCore}
@@ -222,8 +214,18 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
name: hass.config.location_name,
})}
@click=${this._editHomeZone}
slot="meta"
></ha-icon-button>
</ha-tooltip>`}
<ha-tooltip
.for="zone-${slugify(stateObject.entity_id)}"
placement="left"
.disabled=${stateObject.entity_id === "zone.home"}
hoist
>
${hass.localize(
"ui.panel.config.zone.configured_in_yaml"
)}
</ha-tooltip>`}
</ha-list-item>
`
)}

View File

@@ -133,14 +133,12 @@ class HuiEnergyCarbonGaugeCard
"--gauge-color": this._computeSeverity(value),
})}
></ha-gauge>
<ha-tooltip
.content=${this.hass.localize(
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
<ha-tooltip for="info" placement="left">
${this.hass.localize(
"ui.panel.lovelace.cards.energy.carbon_consumed_gauge.card_indicates_energy_used"
)}
placement="left"
hoist
>
<ha-svg-icon .path=${mdiInformation}></ha-svg-icon>
</ha-tooltip>
<div class="name">
${this.hass.localize(

View File

@@ -114,17 +114,15 @@ class HuiEnergyGridGaugeCard
label="kWh"
needle
></ha-gauge>
<ha-tooltip placement="left" hoist>
<span slot="content">
${this.hass.localize(
"ui.panel.lovelace.cards.energy.grid_neutrality_gauge.energy_dependency"
)}
<br /><br />
${this.hass.localize(
"ui.panel.lovelace.cards.energy.grid_neutrality_gauge.color_explain"
)}
</span>
<ha-svg-icon .path=${mdiInformation}></ha-svg-icon>
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
<ha-tooltip for="info" placement="left">
${this.hass.localize(
"ui.panel.lovelace.cards.energy.grid_neutrality_gauge.energy_dependency"
)}
<br /><br />
${this.hass.localize(
"ui.panel.lovelace.cards.energy.grid_neutrality_gauge.color_explain"
)}
</ha-tooltip>
<div class="name">
${returnedToGrid! >= consumedFromGrid!

View File

@@ -110,14 +110,11 @@ class HuiEnergySelfSufficiencyGaugeCard
"--gauge-color": this._computeSeverity(value),
})}
></ha-gauge>
<ha-tooltip
placement="left"
.content=${this.hass.localize(
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
<ha-tooltip for="info" placement="left">
${this.hass.localize(
"ui.panel.lovelace.cards.energy.self_sufficiency_gauge.card_indicates_self_sufficiency_quota"
)}
hoist
>
<ha-svg-icon .path=${mdiInformation}></ha-svg-icon>
</ha-tooltip>
<div class="name">
${this.hass.localize(

View File

@@ -102,17 +102,15 @@ class HuiEnergySolarGaugeCard
"--gauge-color": this._computeSeverity(value),
})}
></ha-gauge>
<ha-tooltip placement="left" hoist>
<span slot="content">
${this.hass.localize(
"ui.panel.lovelace.cards.energy.solar_consumed_gauge.card_indicates_solar_energy_used"
)}
<br /><br />
${this.hass.localize(
"ui.panel.lovelace.cards.energy.solar_consumed_gauge.card_indicates_solar_energy_used_charge_home_bat"
)}
</span>
<ha-svg-icon .path=${mdiInformation}></ha-svg-icon>
<ha-svg-icon id="info" .path=${mdiInformation}></ha-svg-icon>
<ha-tooltip for="info" placement="left">
${this.hass.localize(
"ui.panel.lovelace.cards.energy.solar_consumed_gauge.card_indicates_solar_energy_used"
)}
<br /><br />
${this.hass.localize(
"ui.panel.lovelace.cards.energy.solar_consumed_gauge.card_indicates_solar_energy_used_charge_home_bat"
)}
</ha-tooltip>
<div class="name">
${this.hass.localize(
@@ -176,10 +174,6 @@ class HuiEnergySolarGaugeCard
top: 4px;
color: var(--secondary-text-color);
}
ha-tooltip::part(base__popup) {
margin-top: 4px;
}
`;
}

View File

@@ -47,6 +47,7 @@ import "../../components/ha-menu-button";
import "../../components/ha-svg-icon";
import "../../components/ha-tab-group";
import "../../components/ha-tab-group-tab";
import "../../components/ha-tooltip";
import { createAreaRegistryEntry } from "../../data/area_registry";
import type { LovelacePanelConfig } from "../../data/lovelace";
import type { LovelaceConfig } from "../../data/lovelace/config/types";
@@ -307,7 +308,7 @@ class HUIRoot extends LitElement {
(i) => i.visible && (!i.overflow || overflowCanPromote)
);
buttonItems.forEach((item) => {
buttonItems.forEach((item, index) => {
const label = [this.hass!.localize(item.key), item.suffix].join(" ");
const button = item.subItems
? html`
@@ -341,11 +342,14 @@ class HUIRoot extends LitElement {
</ha-button-menu>
`
: html`
<ha-tooltip slot="actionItems" placement="bottom" .content=${label}>
<ha-icon-button
.path=${item.icon}
@click=${item.buttonAction}
></ha-icon-button>
<ha-icon-button
slot="actionItems"
.id="button-${index}"
.path=${item.icon}
@click=${item.buttonAction}
></ha-icon-button>
<ha-tooltip placement="bottom" .for="button-${index}">
${label}
</ha-tooltip>
`;
result.push(button);

View File

@@ -1644,7 +1644,7 @@ __metadata:
languageName: node
linkType: hard
"@floating-ui/dom@npm:^1.6.12, @floating-ui/dom@npm:^1.6.13":
"@floating-ui/dom@npm:^1.6.13":
version: 1.7.3
resolution: "@floating-ui/dom@npm:1.7.3"
dependencies:
@@ -1905,9 +1905,9 @@ __metadata:
languageName: node
linkType: hard
"@home-assistant/webawesome@npm:3.0.0-beta.4.ha.2":
version: 3.0.0-beta.4.ha.2
resolution: "@home-assistant/webawesome@npm:3.0.0-beta.4.ha.2"
"@home-assistant/webawesome@npm:3.0.0-beta.4.ha.3":
version: 3.0.0-beta.4.ha.3
resolution: "@home-assistant/webawesome@npm:3.0.0-beta.4.ha.3"
dependencies:
"@ctrl/tinycolor": "npm:^4.1.0"
"@floating-ui/dom": "npm:^1.6.13"
@@ -1919,7 +1919,7 @@ __metadata:
nanoid: "npm:^5.1.5"
qr-creator: "npm:^1.0.0"
style-observer: "npm:^0.0.7"
checksum: 10/0ac66d43050571e2b86bb7b0181d428aa2a064e25745075b207a8fe96d873398eaead663172130dfe8d9ac0be575028f8f9f6b9f8a9cd12f81c8c82e9f60a0e9
checksum: 10/b9241821ed471ccbad86b0ea4697a2d41395f05fdc26f46e5edbc7f6b5eeab5d248251ef702326312ded00d5bf850ce0dcdcf7cd5e2e542b9d9cb9a84f3726da
languageName: node
linkType: hard
@@ -2326,7 +2326,7 @@ __metadata:
languageName: node
linkType: hard
"@lit/react@npm:^1.0.6, @lit/react@npm:^1.0.8":
"@lit/react@npm:^1.0.8":
version: 1.0.8
resolution: "@lit/react@npm:1.0.8"
peerDependencies:
@@ -4170,22 +4170,6 @@ __metadata:
languageName: node
linkType: hard
"@shoelace-style/shoelace@npm:2.20.1":
version: 2.20.1
resolution: "@shoelace-style/shoelace@npm:2.20.1"
dependencies:
"@ctrl/tinycolor": "npm:^4.1.0"
"@floating-ui/dom": "npm:^1.6.12"
"@lit/react": "npm:^1.0.6"
"@shoelace-style/animations": "npm:^1.2.0"
"@shoelace-style/localize": "npm:^3.2.1"
composed-offset-position: "npm:^0.0.6"
lit: "npm:^3.2.1"
qr-creator: "npm:^1.0.0"
checksum: 10/c3aabeac03d5fd5bc43799783562ab09c92bae98efbc43a931c7dcec608acc393771b6ed0da3f29e08570bb9d9a9e3bff7637cbf6f79ba7aa439f6641da4eb7c
languageName: node
linkType: hard
"@sindresorhus/merge-streams@npm:^2.1.0":
version: 2.3.0
resolution: "@sindresorhus/merge-streams@npm:2.3.0"
@@ -9334,7 +9318,7 @@ __metadata:
"@fullcalendar/list": "npm:6.1.19"
"@fullcalendar/luxon3": "npm:6.1.19"
"@fullcalendar/timegrid": "npm:6.1.19"
"@home-assistant/webawesome": "npm:3.0.0-beta.4.ha.2"
"@home-assistant/webawesome": "npm:3.0.0-beta.4.ha.3"
"@lezer/highlight": "npm:1.2.1"
"@lit-labs/motion": "npm:1.0.9"
"@lit-labs/observers": "npm:2.0.6"
@@ -9374,7 +9358,6 @@ __metadata:
"@rsdoctor/rspack-plugin": "npm:1.2.3"
"@rspack/core": "npm:1.5.2"
"@rspack/dev-server": "npm:1.1.4"
"@shoelace-style/shoelace": "npm:2.20.1"
"@swc/helpers": "npm:0.5.17"
"@thomasloven/round-slider": "npm:0.6.0"
"@tsparticles/engine": "npm:3.9.1"