mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Use Material 3 ripple (#20751)
* Use material web ripple component * Improve button style * Use css animation instead of ripple for action * Use ha ripple in all components * Remove unused label
This commit is contained in:
parent
505d7b6ddb
commit
4a77359a06
@ -70,7 +70,6 @@
|
|||||||
"@material/mwc-list": "0.27.0",
|
"@material/mwc-list": "0.27.0",
|
||||||
"@material/mwc-menu": "0.27.0",
|
"@material/mwc-menu": "0.27.0",
|
||||||
"@material/mwc-radio": "0.27.0",
|
"@material/mwc-radio": "0.27.0",
|
||||||
"@material/mwc-ripple": "0.27.0",
|
|
||||||
"@material/mwc-select": "0.27.0",
|
"@material/mwc-select": "0.27.0",
|
||||||
"@material/mwc-snackbar": "0.27.0",
|
"@material/mwc-snackbar": "0.27.0",
|
||||||
"@material/mwc-switch": "0.27.0",
|
"@material/mwc-switch": "0.27.0",
|
||||||
|
@ -1,14 +1,7 @@
|
|||||||
import { Ripple } from "@material/mwc-ripple";
|
|
||||||
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import {
|
import { customElement, property } from "lit/decorators";
|
||||||
customElement,
|
|
||||||
eventOptions,
|
|
||||||
property,
|
|
||||||
queryAsync,
|
|
||||||
state,
|
|
||||||
} from "lit/decorators";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import "./ha-ripple";
|
||||||
|
|
||||||
@customElement("ha-control-button")
|
@customElement("ha-control-button")
|
||||||
export class HaControlButton extends LitElement {
|
export class HaControlButton extends LitElement {
|
||||||
@ -16,10 +9,6 @@ export class HaControlButton extends LitElement {
|
|||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
|
||||||
|
|
||||||
@state() private _shouldRenderRipple = false;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<button
|
<button
|
||||||
@ -28,54 +17,13 @@ export class HaControlButton extends LitElement {
|
|||||||
aria-label=${ifDefined(this.label)}
|
aria-label=${ifDefined(this.label)}
|
||||||
title=${ifDefined(this.label)}
|
title=${ifDefined(this.label)}
|
||||||
.disabled=${Boolean(this.disabled)}
|
.disabled=${Boolean(this.disabled)}
|
||||||
@focus=${this.handleRippleFocus}
|
|
||||||
@blur=${this.handleRippleBlur}
|
|
||||||
@mousedown=${this.handleRippleActivate}
|
|
||||||
@mouseup=${this.handleRippleDeactivate}
|
|
||||||
@mouseenter=${this.handleRippleMouseEnter}
|
|
||||||
@mouseleave=${this.handleRippleMouseLeave}
|
|
||||||
@touchstart=${this.handleRippleActivate}
|
|
||||||
@touchend=${this.handleRippleDeactivate}
|
|
||||||
@touchcancel=${this.handleRippleDeactivate}
|
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
${this._shouldRenderRipple && !this.disabled
|
<ha-ripple .disabled=${this.disabled}></ha-ripple>
|
||||||
? html`<mwc-ripple></mwc-ripple>`
|
|
||||||
: ""}
|
|
||||||
</button>
|
</button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
|
||||||
this._shouldRenderRipple = true;
|
|
||||||
return this._ripple;
|
|
||||||
});
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleActivate(evt?: Event) {
|
|
||||||
this._rippleHandlers.startPress(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleDeactivate() {
|
|
||||||
this._rippleHandlers.endPress();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseEnter() {
|
|
||||||
this._rippleHandlers.startHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseLeave() {
|
|
||||||
this._rippleHandlers.endHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleFocus() {
|
|
||||||
this._rippleHandlers.startFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleBlur() {
|
|
||||||
this._rippleHandlers.endFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
@ -86,6 +34,7 @@ export class HaControlButton extends LitElement {
|
|||||||
--control-button-border-radius: 10px;
|
--control-button-border-radius: 10px;
|
||||||
--control-button-padding: 8px;
|
--control-button-padding: 8px;
|
||||||
--mdc-icon-size: 20px;
|
--mdc-icon-size: 20px;
|
||||||
|
--ha-ripple-color: var(--secondary-text-color);
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@ -113,12 +62,14 @@ export class HaControlButton extends LitElement {
|
|||||||
outline: none;
|
outline: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: none;
|
background: none;
|
||||||
--mdc-ripple-color: var(--control-button-background-color);
|
|
||||||
/* For safari border-radius overflow */
|
/* For safari border-radius overflow */
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
.button:focus-visible {
|
||||||
|
--control-button-background-opacity: 0.4;
|
||||||
|
}
|
||||||
.button::before {
|
.button::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -1,22 +1,14 @@
|
|||||||
import { Ripple } from "@material/mwc-ripple";
|
|
||||||
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
|
||||||
import { SelectBase } from "@material/mwc-select/mwc-select-base";
|
import { SelectBase } from "@material/mwc-select/mwc-select-base";
|
||||||
import { mdiMenuDown } from "@mdi/js";
|
import { mdiMenuDown } from "@mdi/js";
|
||||||
import { css, html, nothing } from "lit";
|
import { css, html, nothing } from "lit";
|
||||||
import {
|
import { customElement, property, query } from "lit/decorators";
|
||||||
customElement,
|
|
||||||
eventOptions,
|
|
||||||
property,
|
|
||||||
query,
|
|
||||||
queryAsync,
|
|
||||||
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 { debounce } from "../common/util/debounce";
|
import { debounce } from "../common/util/debounce";
|
||||||
import { nextRender } from "../common/util/render-status";
|
import { nextRender } from "../common/util/render-status";
|
||||||
import "./ha-icon";
|
import "./ha-icon";
|
||||||
import type { HaIcon } from "./ha-icon";
|
import type { HaIcon } from "./ha-icon";
|
||||||
|
import "./ha-ripple";
|
||||||
import "./ha-svg-icon";
|
import "./ha-svg-icon";
|
||||||
import type { HaSvgIcon } from "./ha-svg-icon";
|
import type { HaSvgIcon } from "./ha-svg-icon";
|
||||||
|
|
||||||
@ -32,10 +24,6 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
@property({ type: Boolean, attribute: "hide-label" })
|
@property({ type: Boolean, attribute: "hide-label" })
|
||||||
public hideLabel = false;
|
public hideLabel = false;
|
||||||
|
|
||||||
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
|
||||||
|
|
||||||
@state() private _shouldRenderRipple = false;
|
|
||||||
|
|
||||||
public override render() {
|
public override render() {
|
||||||
const classes = {
|
const classes = {
|
||||||
"select-disabled": this.disabled,
|
"select-disabled": this.disabled,
|
||||||
@ -69,17 +57,10 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
aria-labelledby=${ifDefined(labelledby)}
|
aria-labelledby=${ifDefined(labelledby)}
|
||||||
aria-label=${ifDefined(labelAttribute)}
|
aria-label=${ifDefined(labelAttribute)}
|
||||||
aria-required=${this.required}
|
aria-required=${this.required}
|
||||||
@click=${this.onClick}
|
|
||||||
@focus=${this.onFocus}
|
@focus=${this.onFocus}
|
||||||
@blur=${this.onBlur}
|
@blur=${this.onBlur}
|
||||||
|
@click=${this.onClick}
|
||||||
@keydown=${this.onKeydown}
|
@keydown=${this.onKeydown}
|
||||||
@mousedown=${this.handleRippleActivate}
|
|
||||||
@mouseup=${this.handleRippleDeactivate}
|
|
||||||
@mouseenter=${this.handleRippleMouseEnter}
|
|
||||||
@mouseleave=${this.handleRippleMouseLeave}
|
|
||||||
@touchstart=${this.handleRippleActivate}
|
|
||||||
@touchend=${this.handleRippleDeactivate}
|
|
||||||
@touchcancel=${this.handleRippleDeactivate}
|
|
||||||
>
|
>
|
||||||
${this.renderIcon()}
|
${this.renderIcon()}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@ -91,9 +72,7 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
${this.renderArrow()}
|
${this.renderArrow()}
|
||||||
${this._shouldRenderRipple && !this.disabled
|
<ha-ripple .disabled=${this.disabled}></ha-ripple>
|
||||||
? html` <mwc-ripple></mwc-ripple> `
|
|
||||||
: nothing}
|
|
||||||
</div>
|
</div>
|
||||||
${this.renderMenu()}
|
${this.renderMenu()}
|
||||||
</div>
|
</div>
|
||||||
@ -135,46 +114,6 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onFocus() {
|
|
||||||
this.handleRippleFocus();
|
|
||||||
super.onFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected onBlur() {
|
|
||||||
this.handleRippleBlur();
|
|
||||||
super.onBlur();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
|
||||||
this._shouldRenderRipple = true;
|
|
||||||
return this._ripple;
|
|
||||||
});
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleActivate(evt?: Event) {
|
|
||||||
this._rippleHandlers.startPress(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleDeactivate() {
|
|
||||||
this._rippleHandlers.endPress();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseEnter() {
|
|
||||||
this._rippleHandlers.startHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseLeave() {
|
|
||||||
this._rippleHandlers.endHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleFocus() {
|
|
||||||
this._rippleHandlers.startFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleBlur() {
|
|
||||||
this._rippleHandlers.endFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
window.addEventListener("translations-updated", this._translationsUpdated);
|
window.addEventListener("translations-updated", this._translationsUpdated);
|
||||||
@ -204,6 +143,7 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
--control-select-menu-height: 48px;
|
--control-select-menu-height: 48px;
|
||||||
--control-select-menu-padding: 6px 10px;
|
--control-select-menu-padding: 6px 10px;
|
||||||
--mdc-icon-size: 20px;
|
--mdc-icon-size: 20px;
|
||||||
|
--ha-ripple-color: var(--secondary-text-color);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
width: auto;
|
width: auto;
|
||||||
@ -224,7 +164,6 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
outline: none;
|
outline: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: none;
|
background: none;
|
||||||
--mdc-ripple-color: var(--control-select-menu-background-color);
|
|
||||||
/* For safari border-radius overflow */
|
/* For safari border-radius overflow */
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
transition: color 180ms ease-in-out;
|
transition: color 180ms ease-in-out;
|
||||||
@ -264,6 +203,10 @@ export class HaControlSelectMenu extends SelectBase {
|
|||||||
letter-spacing: inherit;
|
letter-spacing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.select-anchor:focus-visible {
|
||||||
|
--control-select-menu-background-opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
.select-anchor::before {
|
.select-anchor::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import "@material/web/ripple/ripple";
|
|
||||||
|
|
||||||
@customElement("ha-label")
|
@customElement("ha-label")
|
||||||
class HaLabel extends LitElement {
|
class HaLabel extends LitElement {
|
||||||
@ -11,7 +10,6 @@ class HaLabel extends LitElement {
|
|||||||
<span class="content">
|
<span class="content">
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<md-ripple></md-ripple>
|
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -27,7 +25,6 @@ class HaLabel extends LitElement {
|
|||||||
0.15
|
0.15
|
||||||
);
|
);
|
||||||
--ha-label-background-opacity: 1;
|
--ha-label-background-opacity: 1;
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
63
src/components/ha-ripple.ts
Normal file
63
src/components/ha-ripple.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import { AttachableController } from "@material/web/internal/controller/attachable-controller";
|
||||||
|
import { MdRipple } from "@material/web/ripple/ripple";
|
||||||
|
import "element-internals-polyfill";
|
||||||
|
import { css } from "lit";
|
||||||
|
import { customElement } from "lit/decorators";
|
||||||
|
|
||||||
|
@customElement("ha-ripple")
|
||||||
|
export class HaRipple extends MdRipple {
|
||||||
|
private readonly attachableTouchController = new AttachableController(
|
||||||
|
this,
|
||||||
|
this.onTouchControlChange.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
attach(control: HTMLElement) {
|
||||||
|
super.attach(control);
|
||||||
|
this.attachableTouchController.attach(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
super.detach();
|
||||||
|
this.attachableTouchController.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleTouchEnd = () => {
|
||||||
|
if (!this.disabled) {
|
||||||
|
// @ts-ignore
|
||||||
|
super.endPressAnimation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private onTouchControlChange(
|
||||||
|
prev: HTMLElement | null,
|
||||||
|
next: HTMLElement | null
|
||||||
|
) {
|
||||||
|
// Add touchend event to clean ripple on touch devices using action handler
|
||||||
|
prev?.removeEventListener("touchend", this._handleTouchEnd);
|
||||||
|
next?.addEventListener("touchend", this._handleTouchEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static override styles = [
|
||||||
|
...super.styles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
--md-ripple-hover-opacity: var(--ha-ripple-hover-opacity, 0.08);
|
||||||
|
--md-ripple-pressed-opacity: var(--ha-ripple-pressed-opacity, 0.12);
|
||||||
|
--md-ripple-hover-color: var(
|
||||||
|
--ha-ripple-hover-color,
|
||||||
|
var(--ha-ripple-color, var(--secondary-text-color))
|
||||||
|
);
|
||||||
|
--md-ripple-pressed-color: var(
|
||||||
|
--ha-ripple-pressed-color,
|
||||||
|
var(--ha-ripple-color, var(--secondary-text-color))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-ripple": HaRipple;
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,7 @@
|
|||||||
import type { Ripple } from "@material/mwc-ripple";
|
|
||||||
import "@material/mwc-ripple/mwc-ripple";
|
|
||||||
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import {
|
import { customElement, property } from "lit/decorators";
|
||||||
customElement,
|
|
||||||
eventOptions,
|
|
||||||
property,
|
|
||||||
queryAsync,
|
|
||||||
state,
|
|
||||||
} from "lit/decorators";
|
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import "./ha-ripple";
|
||||||
|
|
||||||
@customElement("ha-tab")
|
@customElement("ha-tab")
|
||||||
export class HaTab extends LitElement {
|
export class HaTab extends LitElement {
|
||||||
@ -19,10 +11,6 @@ export class HaTab extends LitElement {
|
|||||||
|
|
||||||
@property() public name?: string;
|
@property() public name?: string;
|
||||||
|
|
||||||
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
|
||||||
|
|
||||||
@state() private _shouldRenderRipple = false;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
@ -30,60 +18,21 @@ export class HaTab extends LitElement {
|
|||||||
role="tab"
|
role="tab"
|
||||||
aria-selected=${this.active}
|
aria-selected=${this.active}
|
||||||
aria-label=${ifDefined(this.name)}
|
aria-label=${ifDefined(this.name)}
|
||||||
@focus=${this.handleRippleFocus}
|
|
||||||
@blur=${this.handleRippleBlur}
|
|
||||||
@mousedown=${this.handleRippleActivate}
|
|
||||||
@mouseup=${this.handleRippleDeactivate}
|
|
||||||
@mouseenter=${this.handleRippleMouseEnter}
|
|
||||||
@mouseleave=${this.handleRippleMouseLeave}
|
|
||||||
@touchstart=${this.handleRippleActivate}
|
|
||||||
@touchend=${this.handleRippleDeactivate}
|
|
||||||
@touchcancel=${this.handleRippleDeactivate}
|
|
||||||
@keydown=${this._handleKeyDown}
|
@keydown=${this._handleKeyDown}
|
||||||
>
|
>
|
||||||
${this.narrow ? html`<slot name="icon"></slot>` : ""}
|
${this.narrow ? html`<slot name="icon"></slot>` : ""}
|
||||||
<span class="name">${this.name}</span>
|
<span class="name">${this.name}</span>
|
||||||
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ""}
|
<ha-ripple></ha-ripple>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
|
||||||
this._shouldRenderRipple = true;
|
|
||||||
return this._ripple;
|
|
||||||
});
|
|
||||||
|
|
||||||
private _handleKeyDown(ev: KeyboardEvent): void {
|
private _handleKeyDown(ev: KeyboardEvent): void {
|
||||||
if (ev.key === "Enter") {
|
if (ev.key === "Enter") {
|
||||||
(ev.target as HTMLElement).click();
|
(ev.target as HTMLElement).click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleActivate(evt?: Event) {
|
|
||||||
this._rippleHandlers.startPress(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleDeactivate() {
|
|
||||||
this._rippleHandlers.endPress();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseEnter() {
|
|
||||||
this._rippleHandlers.startHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseLeave() {
|
|
||||||
this._rippleHandlers.endHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleFocus() {
|
|
||||||
this._rippleHandlers.startFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleBlur() {
|
|
||||||
this._rippleHandlers.endFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
div {
|
div {
|
||||||
@ -126,6 +75,15 @@ export class HaTab extends LitElement {
|
|||||||
:host([narrow]) div {
|
:host([narrow]) div {
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div:focus-visible:before {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
inset: 0;
|
||||||
|
background-color: var(--secondary-text-color);
|
||||||
|
opacity: 0.08;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import "@material/mwc-ripple";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
|
@ -1,15 +1,7 @@
|
|||||||
import "@material/mwc-ripple";
|
|
||||||
import type { Ripple } from "@material/mwc-ripple";
|
|
||||||
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||||
import {
|
import { customElement, property } from "lit/decorators";
|
||||||
customElement,
|
|
||||||
eventOptions,
|
|
||||||
property,
|
|
||||||
queryAsync,
|
|
||||||
state,
|
|
||||||
} from "lit/decorators";
|
|
||||||
import "../components/ha-card";
|
import "../components/ha-card";
|
||||||
|
import "../components/ha-ripple";
|
||||||
import "../components/ha-svg-icon";
|
import "../components/ha-svg-icon";
|
||||||
|
|
||||||
@customElement("onboarding-welcome-link")
|
@customElement("onboarding-welcome-link")
|
||||||
@ -20,28 +12,15 @@ class OnboardingWelcomeLink extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public noninteractive = false;
|
@property({ type: Boolean }) public noninteractive = false;
|
||||||
|
|
||||||
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
|
||||||
|
|
||||||
@state() private _shouldRenderRipple = false;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
.tabIndex=${this.noninteractive ? "-1" : "0"}
|
.tabIndex=${this.noninteractive ? "-1" : "0"}
|
||||||
@focus=${this.handleRippleFocus}
|
|
||||||
@blur=${this.handleRippleBlur}
|
|
||||||
@mousedown=${this.handleRippleActivate}
|
|
||||||
@mouseup=${this.handleRippleDeactivate}
|
|
||||||
@mouseenter=${this.handleRippleMouseEnter}
|
|
||||||
@mouseleave=${this.handleRippleMouseLeave}
|
|
||||||
@touchstart=${this.handleRippleActivate}
|
|
||||||
@touchend=${this.handleRippleDeactivate}
|
|
||||||
@touchcancel=${this.handleRippleDeactivate}
|
|
||||||
@keydown=${this._handleKeyDown}
|
@keydown=${this._handleKeyDown}
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${this.iconPath}></ha-svg-icon>
|
<ha-svg-icon .path=${this.iconPath}></ha-svg-icon>
|
||||||
${this.label}
|
${this.label}
|
||||||
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ""}
|
<ha-ripple></ha-ripple>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -52,36 +31,6 @@ class OnboardingWelcomeLink extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
|
||||||
this._shouldRenderRipple = true;
|
|
||||||
return this._ripple;
|
|
||||||
});
|
|
||||||
|
|
||||||
private handleRippleMouseEnter() {
|
|
||||||
this._rippleHandlers.startHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseLeave() {
|
|
||||||
this._rippleHandlers.endHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleActivate(evt?: Event) {
|
|
||||||
this._rippleHandlers.startPress(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleDeactivate() {
|
|
||||||
this._rippleHandlers.endPress();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleFocus() {
|
|
||||||
this._rippleHandlers.startFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleBlur() {
|
|
||||||
this._rippleHandlers.endFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
@ -104,6 +53,14 @@ class OnboardingWelcomeLink extends LitElement {
|
|||||||
padding: 8px;
|
padding: 8px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
ha-card:focus-visible:before {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
inset: 0;
|
||||||
|
background-color: var(--secondary-text-color);
|
||||||
|
opacity: 0.08;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||||
import "@material/mwc-ripple";
|
|
||||||
import type { Ripple } from "@material/mwc-ripple";
|
|
||||||
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
|
||||||
import { mdiCloud, mdiPackageVariant } from "@mdi/js";
|
import { mdiCloud, mdiPackageVariant } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@ -11,18 +8,13 @@ import {
|
|||||||
html,
|
html,
|
||||||
nothing,
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import {
|
import { customElement, property } from "lit/decorators";
|
||||||
customElement,
|
|
||||||
eventOptions,
|
|
||||||
property,
|
|
||||||
queryAsync,
|
|
||||||
state,
|
|
||||||
} from "lit/decorators";
|
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
import "../../../components/ha-card";
|
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-ripple";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { ConfigEntry, ERROR_STATES } from "../../../data/config_entries";
|
import { ConfigEntry, ERROR_STATES } from "../../../data/config_entries";
|
||||||
import type { DeviceRegistryEntry } from "../../../data/device_registry";
|
import type { DeviceRegistryEntry } from "../../../data/device_registry";
|
||||||
@ -54,10 +46,6 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public logInfo?: IntegrationLogInfo;
|
@property({ attribute: false }) public logInfo?: IntegrationLogInfo;
|
||||||
|
|
||||||
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
|
||||||
|
|
||||||
@state() private _shouldRenderRipple = false;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const entryState = this._getState(this.items);
|
const entryState = this._getState(this.items);
|
||||||
|
|
||||||
@ -79,17 +67,8 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
<a
|
<a
|
||||||
href=${`/config/integrations/integration/${this.domain}`}
|
href=${`/config/integrations/integration/${this.domain}`}
|
||||||
class="ripple-anchor"
|
class="ripple-anchor"
|
||||||
@focus=${this.handleRippleFocus}
|
|
||||||
@blur=${this.handleRippleBlur}
|
|
||||||
@mouseenter=${this.handleRippleMouseEnter}
|
|
||||||
@mouseleave=${this.handleRippleMouseLeave}
|
|
||||||
@mousedown=${this.handleRippleActivate}
|
|
||||||
@mouseup=${this.handleRippleDeactivate}
|
|
||||||
@touchstart=${this.handleRippleActivate}
|
|
||||||
@touchend=${this.handleRippleDeactivate}
|
|
||||||
@touchcancel=${this.handleRippleDeactivate}
|
|
||||||
>
|
>
|
||||||
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ""}
|
<ha-ripple></ha-ripple>
|
||||||
<ha-integration-header
|
<ha-integration-header
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.domain=${this.domain}
|
.domain=${this.domain}
|
||||||
@ -242,36 +221,6 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
|
||||||
this._shouldRenderRipple = true;
|
|
||||||
return this._ripple;
|
|
||||||
});
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleActivate(evt?: Event) {
|
|
||||||
this._rippleHandlers.startPress(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleDeactivate() {
|
|
||||||
this._rippleHandlers.endPress();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleFocus() {
|
|
||||||
this._rippleHandlers.startFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleBlur() {
|
|
||||||
this._rippleHandlers.endFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected handleRippleMouseEnter() {
|
|
||||||
this._rippleHandlers.startHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected handleRippleMouseLeave() {
|
|
||||||
this._rippleHandlers.endHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@ -289,6 +238,15 @@ export class HaIntegrationCard extends LitElement {
|
|||||||
.ripple-anchor {
|
.ripple-anchor {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.ripple-anchor:focus-visible:before {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
inset: 0;
|
||||||
|
background-color: var(--secondary-text-color);
|
||||||
|
opacity: 0.08;
|
||||||
}
|
}
|
||||||
ha-integration-header {
|
ha-integration-header {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import "@material/mwc-ripple";
|
|
||||||
import {
|
import {
|
||||||
mdiFan,
|
mdiFan,
|
||||||
mdiFanOff,
|
mdiFanOff,
|
||||||
|
@ -1,21 +1,18 @@
|
|||||||
import { consume } from "@lit-labs/context";
|
import { consume } from "@lit-labs/context";
|
||||||
import "@material/mwc-ripple";
|
|
||||||
import type { Ripple } from "@material/mwc-ripple";
|
|
||||||
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
|
||||||
import {
|
import {
|
||||||
HassConfig,
|
HassConfig,
|
||||||
HassEntities,
|
HassEntities,
|
||||||
HassEntity,
|
HassEntity,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
html,
|
|
||||||
LitElement,
|
LitElement,
|
||||||
nothing,
|
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, eventOptions, queryAsync, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { DOMAINS_TOGGLE } from "../../../common/const";
|
import { DOMAINS_TOGGLE } from "../../../common/const";
|
||||||
@ -27,13 +24,14 @@ import { computeStateDisplaySingleEntity } from "../../../common/entity/compute_
|
|||||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import {
|
import {
|
||||||
stateColorCss,
|
|
||||||
stateColorBrightness,
|
stateColorBrightness,
|
||||||
|
stateColorCss,
|
||||||
} from "../../../common/entity/state_color";
|
} from "../../../common/entity/state_color";
|
||||||
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
|
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
|
||||||
import { iconColorCSS } from "../../../common/style/icon_color_css";
|
import { iconColorCSS } from "../../../common/style/icon_color_css";
|
||||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-ripple";
|
||||||
import { CLIMATE_HVAC_ACTION_TO_MODE } from "../../../data/climate";
|
import { CLIMATE_HVAC_ACTION_TO_MODE } from "../../../data/climate";
|
||||||
import {
|
import {
|
||||||
configContext,
|
configContext,
|
||||||
@ -132,10 +130,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
})
|
})
|
||||||
_entity?: EntityRegistryDisplayEntry;
|
_entity?: EntityRegistryDisplayEntry;
|
||||||
|
|
||||||
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
|
||||||
|
|
||||||
@state() private _shouldRenderRipple = false;
|
|
||||||
|
|
||||||
private getStateColor(stateObj: HassEntity, config: ButtonCardConfig) {
|
private getStateColor(stateObj: HassEntity, config: ButtonCardConfig) {
|
||||||
const domain = stateObj ? computeStateDomain(stateObj) : undefined;
|
const domain = stateObj ? computeStateDomain(stateObj) : undefined;
|
||||||
return config && (config.state_color ?? domain === "light");
|
return config && (config.state_color ?? domain === "light");
|
||||||
@ -197,13 +191,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
@mousedown=${this.handleRippleActivate}
|
|
||||||
@mouseup=${this.handleRippleDeactivate}
|
|
||||||
@mouseenter=${this.handleRippleMouseEnter}
|
|
||||||
@mouseleave=${this.handleRippleMouseLeave}
|
|
||||||
@touchstart=${this.handleRippleActivate}
|
|
||||||
@touchend=${this.handleRippleDeactivate}
|
|
||||||
@touchcancel=${this.handleRippleDeactivate}
|
|
||||||
.actionHandler=${actionHandler({
|
.actionHandler=${actionHandler({
|
||||||
hasHold: hasAction(this._config!.hold_action),
|
hasHold: hasAction(this._config!.hold_action),
|
||||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||||
@ -218,6 +205,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
"--state-color": colored ? this._computeColor(stateObj) : undefined,
|
"--state-color": colored ? this._computeColor(stateObj) : undefined,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
<ha-ripple></ha-ripple>
|
||||||
${this._config.show_icon
|
${this._config.show_icon
|
||||||
? html`
|
? html`
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
@ -252,7 +240,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
)}
|
)}
|
||||||
</span>`
|
</span>`
|
||||||
: ""}
|
: ""}
|
||||||
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : ""}
|
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -282,31 +269,6 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
|
||||||
this._shouldRenderRipple = true;
|
|
||||||
return this._ripple;
|
|
||||||
});
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleActivate(evt?: Event) {
|
|
||||||
this._rippleHandlers.startPress(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleDeactivate() {
|
|
||||||
this._rippleHandlers.endPress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleMouseEnter() {
|
|
||||||
this._rippleHandlers.startHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleMouseLeave() {
|
|
||||||
this._rippleHandlers.endHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
iconColorCSS,
|
iconColorCSS,
|
||||||
@ -314,7 +276,9 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
ha-card {
|
ha-card {
|
||||||
--state-inactive-color: var(--paper-item-icon-color, #44739e);
|
--state-inactive-color: var(--paper-item-icon-color, #44739e);
|
||||||
--state-color: var(--paper-item-icon-color, #44739e);
|
--state-color: var(--paper-item-icon-color, #44739e);
|
||||||
--mdc-ripple-color: var(--state-color);
|
--ha-ripple-color: var(--state-color);
|
||||||
|
--ha-ripple-hover-opacity: 0.04;
|
||||||
|
--ha-ripple-pressed-opacity: 0.12;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -340,6 +304,7 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
|
|||||||
color: var(--state-color);
|
color: var(--state-color);
|
||||||
--mdc-icon-size: 100%;
|
--mdc-icon-size: 100%;
|
||||||
transition: transform 180ms ease-in-out;
|
transition: transform 180ms ease-in-out;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-state-icon + span {
|
ha-state-icon + span {
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import { Ripple } from "@material/mwc-ripple";
|
|
||||||
import { RippleHandlers } from "@material/mwc-ripple/ripple-handlers";
|
|
||||||
import { mdiExclamationThick, mdiHelp } from "@mdi/js";
|
import { mdiExclamationThick, mdiHelp } from "@mdi/js";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
@ -10,13 +8,7 @@ import {
|
|||||||
html,
|
html,
|
||||||
nothing,
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import {
|
import { customElement, property, state } from "lit/decorators";
|
||||||
customElement,
|
|
||||||
eventOptions,
|
|
||||||
property,
|
|
||||||
queryAsync,
|
|
||||||
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 { styleMap } from "lit/directives/style-map";
|
||||||
@ -29,6 +21,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
|||||||
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-card";
|
||||||
|
import "../../../components/ha-ripple";
|
||||||
import "../../../components/ha-state-icon";
|
import "../../../components/ha-state-icon";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import "../../../components/tile/ha-tile-badge";
|
import "../../../components/tile/ha-tile-badge";
|
||||||
@ -313,36 +306,6 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
return this._renderStateContent(stateObj, "state");
|
return this._renderStateContent(stateObj, "state");
|
||||||
}
|
}
|
||||||
|
|
||||||
@queryAsync("mwc-ripple") private _ripple!: Promise<Ripple | null>;
|
|
||||||
|
|
||||||
@state() private _shouldRenderRipple = false;
|
|
||||||
|
|
||||||
private _rippleHandlers: RippleHandlers = new RippleHandlers(() => {
|
|
||||||
this._shouldRenderRipple = true;
|
|
||||||
return this._ripple;
|
|
||||||
});
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private handleRippleActivate(evt?: Event) {
|
|
||||||
if (!this.hasCardAction) return;
|
|
||||||
this._rippleHandlers.startPress(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleDeactivate() {
|
|
||||||
if (!this.hasCardAction) return;
|
|
||||||
this._rippleHandlers.endPress();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseEnter() {
|
|
||||||
if (!this.hasCardAction) return;
|
|
||||||
this._rippleHandlers.startHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleRippleMouseLeave() {
|
|
||||||
if (!this.hasCardAction) return;
|
|
||||||
this._rippleHandlers.endHover();
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasCardAction() {
|
get hasCardAction() {
|
||||||
return (
|
return (
|
||||||
!this._config?.tap_action ||
|
!this._config?.tap_action ||
|
||||||
@ -420,17 +383,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
role=${ifDefined(this.hasCardAction ? "button" : undefined)}
|
role=${ifDefined(this.hasCardAction ? "button" : undefined)}
|
||||||
tabindex=${ifDefined(this.hasCardAction ? "0" : undefined)}
|
tabindex=${ifDefined(this.hasCardAction ? "0" : undefined)}
|
||||||
aria-labelledby="info"
|
aria-labelledby="info"
|
||||||
@mousedown=${this.handleRippleActivate}
|
|
||||||
@mouseup=${this.handleRippleDeactivate}
|
|
||||||
@mouseenter=${this.handleRippleMouseEnter}
|
|
||||||
@mouseleave=${this.handleRippleMouseLeave}
|
|
||||||
@touchstart=${this.handleRippleActivate}
|
|
||||||
@touchend=${this.handleRippleDeactivate}
|
|
||||||
@touchcancel=${this.handleRippleDeactivate}
|
|
||||||
>
|
>
|
||||||
${this._shouldRenderRipple
|
<ha-ripple .disabled=${!this.hasCardAction}></ha-ripple>
|
||||||
? html`<mwc-ripple></mwc-ripple>`
|
|
||||||
: nothing}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content ${classMap(contentClasses)}">
|
<div class="content ${classMap(contentClasses)}">
|
||||||
<div
|
<div
|
||||||
@ -494,7 +448,9 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
box-shadow: var(--shadow-default), var(--shadow-focus);
|
||||||
}
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
--mdc-ripple-color: var(--tile-color);
|
--ha-ripple-color: var(--tile-color);
|
||||||
|
--ha-ripple-hover-opacity: 0.04;
|
||||||
|
--ha-ripple-pressed-opacity: 0.12;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
transition:
|
transition:
|
||||||
box-shadow 180ms ease-in-out,
|
box-shadow 180ms ease-in-out,
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
import "@material/mwc-ripple";
|
|
||||||
import type { Ripple } from "@material/mwc-ripple";
|
|
||||||
import { noChange } from "lit";
|
import { noChange } from "lit";
|
||||||
import {
|
import {
|
||||||
AttributePart,
|
AttributePart,
|
||||||
@ -41,8 +39,6 @@ declare global {
|
|||||||
class ActionHandler extends HTMLElement implements ActionHandlerType {
|
class ActionHandler extends HTMLElement implements ActionHandlerType {
|
||||||
public holdTime = 500;
|
public holdTime = 500;
|
||||||
|
|
||||||
public ripple: Ripple;
|
|
||||||
|
|
||||||
protected timer?: number;
|
protected timer?: number;
|
||||||
|
|
||||||
protected held = false;
|
protected held = false;
|
||||||
@ -51,24 +47,21 @@ class ActionHandler extends HTMLElement implements ActionHandlerType {
|
|||||||
|
|
||||||
private dblClickTimeout?: number;
|
private dblClickTimeout?: number;
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.ripple = document.createElement("mwc-ripple");
|
|
||||||
}
|
|
||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
Object.assign(this.style, {
|
Object.assign(this.style, {
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
width: isTouch ? "100px" : "50px",
|
width: isTouch ? "100px" : "50px",
|
||||||
height: isTouch ? "100px" : "50px",
|
height: isTouch ? "100px" : "50px",
|
||||||
transform: "translate(-50%, -50%)",
|
transform: "translate(-50%, -50%) scale(0)",
|
||||||
pointerEvents: "none",
|
pointerEvents: "none",
|
||||||
zIndex: "999",
|
zIndex: "999",
|
||||||
|
background: "var(--primary-color)",
|
||||||
|
display: null,
|
||||||
|
opacity: "0.2",
|
||||||
|
borderRadius: "50%",
|
||||||
|
transition: "transform 180ms ease-in-out",
|
||||||
});
|
});
|
||||||
|
|
||||||
this.appendChild(this.ripple);
|
|
||||||
this.ripple.primary = true;
|
|
||||||
|
|
||||||
[
|
[
|
||||||
"touchcancel",
|
"touchcancel",
|
||||||
"mouseout",
|
"mouseout",
|
||||||
@ -219,17 +212,16 @@ class ActionHandler extends HTMLElement implements ActionHandlerType {
|
|||||||
Object.assign(this.style, {
|
Object.assign(this.style, {
|
||||||
left: `${x}px`,
|
left: `${x}px`,
|
||||||
top: `${y}px`,
|
top: `${y}px`,
|
||||||
display: null,
|
transform: "translate(-50%, -50%) scale(1)",
|
||||||
});
|
});
|
||||||
this.ripple.disabled = false;
|
|
||||||
this.ripple.startPress();
|
|
||||||
this.ripple.unbounded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private stopAnimation() {
|
private stopAnimation() {
|
||||||
this.ripple.endPress();
|
Object.assign(this.style, {
|
||||||
this.ripple.disabled = true;
|
left: null,
|
||||||
this.style.display = "none";
|
top: null,
|
||||||
|
transform: "translate(-50%, -50%) scale(0)",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2742,7 +2742,7 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@material/mwc-ripple@npm:0.27.0, @material/mwc-ripple@npm:^0.27.0":
|
"@material/mwc-ripple@npm:^0.27.0":
|
||||||
version: 0.27.0
|
version: 0.27.0
|
||||||
resolution: "@material/mwc-ripple@npm:0.27.0"
|
resolution: "@material/mwc-ripple@npm:0.27.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -8967,7 +8967,6 @@ __metadata:
|
|||||||
"@material/mwc-list": "npm:0.27.0"
|
"@material/mwc-list": "npm:0.27.0"
|
||||||
"@material/mwc-menu": "npm:0.27.0"
|
"@material/mwc-menu": "npm:0.27.0"
|
||||||
"@material/mwc-radio": "npm:0.27.0"
|
"@material/mwc-radio": "npm:0.27.0"
|
||||||
"@material/mwc-ripple": "npm:0.27.0"
|
|
||||||
"@material/mwc-select": "npm:0.27.0"
|
"@material/mwc-select": "npm:0.27.0"
|
||||||
"@material/mwc-snackbar": "npm:0.27.0"
|
"@material/mwc-snackbar": "npm:0.27.0"
|
||||||
"@material/mwc-switch": "npm:0.27.0"
|
"@material/mwc-switch": "npm:0.27.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user