diff --git a/src/panels/lovelace/cards/hui-media-control-card.ts b/src/panels/lovelace/cards/hui-media-control-card.ts index f41c6198df..a06f631c63 100644 --- a/src/panels/lovelace/cards/hui-media-control-card.ts +++ b/src/panels/lovelace/cards/hui-media-control-card.ts @@ -44,6 +44,7 @@ import { import "../../../components/ha-card"; import "../../../components/ha-icon"; +import "../components/hui-marquee"; function getContrastRatio( rgb1: [number, number, number], @@ -88,6 +89,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { @property() private _narrow: boolean = false; @property() private _veryNarrow: boolean = false; @property() private _cardHeight: number = 0; + @property() private _marqueeActive: boolean = false; private _progressInterval?: number; private _resizeObserver?: ResizeObserver; @@ -240,8 +242,13 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { : html`
- ${stateObj.attributes.media_title || - computeMediaDescription(stateObj)} +
${!stateObj.attributes.media_title ? "" @@ -564,6 +571,18 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard { }); } + private _marqueeMouseOver(): void { + if (!this._marqueeActive) { + this._marqueeActive = true; + } + } + + private _marqueeMouseLeave(): void { + if (this._marqueeActive) { + this._marqueeActive = false; + } + } + static get styles(): CSSResult { return css` ha-card { diff --git a/src/panels/lovelace/components/hui-marquee.ts b/src/panels/lovelace/components/hui-marquee.ts new file mode 100644 index 0000000000..f878d9a808 --- /dev/null +++ b/src/panels/lovelace/components/hui-marquee.ts @@ -0,0 +1,89 @@ +import { + html, + LitElement, + PropertyValues, + TemplateResult, + customElement, + css, + CSSResult, + property, +} from "lit-element"; + +const marqueeSpeed = 0.2; + +@customElement("hui-marquee") +class HuiMarquee extends LitElement { + @property() public text?: string; + @property() public active?: boolean; + private _interval?: number; + private _left: number = 0; + + protected updated(changedProperties: PropertyValues): void { + super.updated(changedProperties); + + if ( + changedProperties.has("active") && + this.active && + !this._interval && + this.offsetWidth < this.scrollWidth + ) { + this._interval = window.setInterval(() => { + this._play(); + }); + + this.requestUpdate(); + } + } + + protected render(): TemplateResult { + if (!this.text) { + return html``; + } + + return html` +
${this.text}
+ ${this._interval + ? html` +
${this.text}
+ ` + : ""} + `; + } + + private get _marqueeElementFirstChild(): HTMLElement { + return this.shadowRoot!.firstElementChild as HTMLElement; + } + + private _play(): void { + this.style.marginLeft = "-" + this._left + "px"; + + if (!this.active && !this._left) { + clearInterval(this._interval); + this._interval = undefined; + return; + } + + this._left += marqueeSpeed; + if (this._left >= this._marqueeElementFirstChild.offsetWidth + 16) { + this._left = 0; + } + } + + static get styles(): CSSResult { + return css` + :host { + display: flex; + } + + :host div { + margin-right: 16px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-marquee": HuiMarquee; + } +}