mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Extract media controls into method (#5141)
* Extract media controls into method * address comments * lint * Moooorre fixes * Fix margin * Update demos * Very narrow idle players show play button * Lint * More stuff * Marquee on steroids
This commit is contained in:
parent
c7a5f63e33
commit
9b220cc6ce
@ -1,23 +1,36 @@
|
||||
import { getEntity } from "../../../src/fake_data/entity";
|
||||
|
||||
export const createMediaPlayerEntities = () => [
|
||||
getEntity("media_player", "bedroom", "playing", {
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
friendly_name: "Skip, no pause",
|
||||
supported_features: 32,
|
||||
}),
|
||||
getEntity("media_player", "family_room", "paused", {
|
||||
friendly_name: "Paused, music",
|
||||
getEntity("media_player", "music_paused", "paused", {
|
||||
friendly_name: "Pausing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
supported_features: 16417,
|
||||
supported_features: 64063,
|
||||
entity_picture: "/images/album_cover.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 50,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
}),
|
||||
getEntity("media_player", "family_room_no_play", "paused", {
|
||||
friendly_name: "Paused, no play",
|
||||
getEntity("media_player", "music_playing", "playing", {
|
||||
friendly_name: "Playing The Music",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
supported_features: 64063,
|
||||
entity_picture: "/images/album_cover.jpg",
|
||||
media_duration: 300,
|
||||
media_position: 0,
|
||||
media_position_updated_at: new Date(
|
||||
// 23 seconds in
|
||||
new Date().getTime() - 23000
|
||||
).toISOString(),
|
||||
}),
|
||||
getEntity("media_player", "stream_playing", "playing", {
|
||||
friendly_name: "Playing the Stream",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
@ -31,25 +44,19 @@ export const createMediaPlayerEntities = () => [
|
||||
app_name: "Netflix",
|
||||
supported_features: 1,
|
||||
}),
|
||||
getEntity("media_player", "lounge_room", "idle", {
|
||||
friendly_name: "Screen casting",
|
||||
media_content_type: "music",
|
||||
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
|
||||
media_artist: "Technohead",
|
||||
supported_features: 1,
|
||||
getEntity("media_player", "sonos_idle", "idle", {
|
||||
friendly_name: "Sonos Idle",
|
||||
supported_features: 64063,
|
||||
}),
|
||||
getEntity("media_player", "theater", "off", {
|
||||
friendly_name: "Chromcast Idle",
|
||||
media_content_type: "movie",
|
||||
media_title: "Epic sax guy 10 hours",
|
||||
app_name: "YouTube",
|
||||
supported_features: 33,
|
||||
friendly_name: "TV Off",
|
||||
supported_features: 161,
|
||||
}),
|
||||
getEntity("media_player", "android_cast", "playing", {
|
||||
friendly_name: "Player Off",
|
||||
friendly_name: "Casting App",
|
||||
media_title: "Android Screen Casting",
|
||||
app_name: "Screen Mirroring",
|
||||
supported_features: 21437,
|
||||
// supported_features: 21437,
|
||||
}),
|
||||
getEntity("media_player", "unavailable", "unavailable", {
|
||||
friendly_name: "Player Unavailable",
|
||||
|
@ -7,24 +7,24 @@ import { createMediaPlayerEntities } from "../data/media_players";
|
||||
|
||||
const CONFIGS = [
|
||||
{
|
||||
heading: "Skip, no pause",
|
||||
heading: "Paused music",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.bedroom
|
||||
entity: media_player.music_paused
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Paused, music",
|
||||
heading: "Playing music",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.family_room
|
||||
entity: media_player.music_playing
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Paused, no play",
|
||||
heading: "Playing stream",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.family_room_no_play
|
||||
entity: media_player.stream_playing
|
||||
`,
|
||||
},
|
||||
{
|
||||
@ -42,10 +42,10 @@ const CONFIGS = [
|
||||
`,
|
||||
},
|
||||
{
|
||||
heading: "Chromcast Idle",
|
||||
heading: "Sonos Idle",
|
||||
config: `
|
||||
- type: media-control
|
||||
entity: media_player.lounge_room
|
||||
entity: media_player.sonos_idle
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
@ -11,17 +11,17 @@ const CONFIGS = [
|
||||
config: `
|
||||
- type: entities
|
||||
entities:
|
||||
- entity: media_player.bedroom
|
||||
name: Skip, no pause
|
||||
- entity: media_player.family_room
|
||||
name: Paused, music
|
||||
- entity: media_player.family_room_no_play
|
||||
- entity: media_player.music_paused
|
||||
name: Paused music
|
||||
- entity: media_player.music_playing
|
||||
name: Playing music
|
||||
- entity: media_player.stream_playing
|
||||
name: Paused, no play
|
||||
- entity: media_player.living_room
|
||||
name: Pause, No skip, tvshow
|
||||
- entity: media_player.android_cast
|
||||
name: Screen casting
|
||||
- entity: media_player.lounge_room
|
||||
- entity: media_player.sonos_idle
|
||||
name: Chromcast Idle
|
||||
- entity: media_player.theater
|
||||
name: Player Off
|
||||
|
@ -83,9 +83,7 @@ export const domainIcon = (domain: string, state?: string): string => {
|
||||
return state && state === "unlocked" ? "hass:lock-open" : "hass:lock";
|
||||
|
||||
case "media_player":
|
||||
return state && state !== "off" && state !== "idle"
|
||||
? "hass:cast-connected"
|
||||
: "hass:cast";
|
||||
return state && state === "playing" ? "hass:cast-connected" : "hass:cast";
|
||||
|
||||
case "zwave":
|
||||
switch (state) {
|
||||
|
@ -14,7 +14,6 @@ export const SUPPORT_SELECT_SOURCE = 2048;
|
||||
export const SUPPORT_STOP = 4096;
|
||||
export const SUPPORTS_PLAY = 16384;
|
||||
export const SUPPORT_SELECT_SOUND_MODE = 65536;
|
||||
export const OFF_STATES = ["off", "idle"];
|
||||
export const CONTRAST_RATIO = 3.5;
|
||||
|
||||
export interface MediaPlayerThumbnail {
|
||||
@ -56,9 +55,7 @@ export const computeMediaDescription = (stateObj: HassEntity): string => {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
secondaryTitle = stateObj.attributes.app_name
|
||||
? stateObj.attributes.app_name
|
||||
: "";
|
||||
secondaryTitle = stateObj.attributes.app_name || "";
|
||||
}
|
||||
|
||||
return secondaryTitle;
|
||||
|
@ -33,7 +33,6 @@ import { findEntities } from "../common/find-entites";
|
||||
import { LovelaceConfig } from "../../../data/lovelace";
|
||||
import { UNAVAILABLE, UNKNOWN } from "../../../data/entity";
|
||||
import {
|
||||
OFF_STATES,
|
||||
SUPPORT_PAUSE,
|
||||
SUPPORT_TURN_ON,
|
||||
SUPPORT_PREVIOUS_TRACK,
|
||||
@ -49,6 +48,8 @@ import {
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
import "../components/hui-marquee";
|
||||
// tslint:disable-next-line: no-duplicate-imports
|
||||
import { PaperIconButtonElement } from "@polymer/paper-icon-button/paper-icon-button";
|
||||
|
||||
function getContrastRatio(
|
||||
rgb1: [number, number, number],
|
||||
@ -57,6 +58,11 @@ function getContrastRatio(
|
||||
return Math.round((contrast(rgb1, rgb2) + Number.EPSILON) * 100) / 100;
|
||||
}
|
||||
|
||||
interface ControlButton {
|
||||
icon: string;
|
||||
action: string;
|
||||
}
|
||||
|
||||
@customElement("hui-media-control-card")
|
||||
export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
@ -118,7 +124,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
return;
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity] as MediaEntity;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
if (!stateObj) {
|
||||
return;
|
||||
@ -130,7 +136,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
stateObj.state === "playing"
|
||||
) {
|
||||
this._progressInterval = window.setInterval(
|
||||
() => this._updateProgressBar(stateObj),
|
||||
() => this._updateProgressBar(),
|
||||
1000
|
||||
);
|
||||
}
|
||||
@ -147,7 +153,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
if (!this.hass || !this._config) {
|
||||
return html``;
|
||||
}
|
||||
const stateObj = this.hass.states[this._config.entity] as MediaEntity;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
if (!stateObj) {
|
||||
return html`
|
||||
@ -162,7 +168,9 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
const imageStyle = {
|
||||
"background-image": `url(${this.hass.hassUrl(this._image)})`,
|
||||
"background-image": this._image
|
||||
? `url(${this.hass.hassUrl(this._image)})`
|
||||
: "none",
|
||||
width: `${this._cardHeight}px`,
|
||||
"background-color": this._backgroundColor || "",
|
||||
};
|
||||
@ -172,12 +180,19 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
width: `${this._cardHeight}px`,
|
||||
};
|
||||
|
||||
const isOffState = OFF_STATES.includes(stateObj.state);
|
||||
const state = stateObj.state;
|
||||
|
||||
const isOffState = state === "off";
|
||||
const isUnavailable =
|
||||
stateObj.state === UNAVAILABLE ||
|
||||
stateObj.state === UNKNOWN ||
|
||||
(stateObj.state === "off" && !supportsFeature(stateObj, SUPPORT_TURN_ON));
|
||||
state === UNAVAILABLE ||
|
||||
state === UNKNOWN ||
|
||||
(state === "off" && !supportsFeature(stateObj, SUPPORT_TURN_ON));
|
||||
const hasNoImage = !this._image;
|
||||
const controls = this._getControls();
|
||||
const showControls =
|
||||
controls && (!this._veryNarrow || isOffState || state === "idle");
|
||||
|
||||
const mediaDescription = computeMediaDescription(stateObj);
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
@ -215,7 +230,8 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
"no-image": hasNoImage,
|
||||
narrow: this._narrow && !this._veryNarrow,
|
||||
off: isOffState || isUnavailable,
|
||||
"no-progress": !this._showProgressBar && !this._veryNarrow,
|
||||
"no-progress": this._veryNarrow || !this._showProgressBar,
|
||||
"no-controls": !showControls,
|
||||
})}"
|
||||
style=${styleMap({ color: this._foregroundColor || "" })}
|
||||
>
|
||||
@ -246,98 +262,35 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
: `${this._cardHeight - 40}px`,
|
||||
})}
|
||||
>
|
||||
${isOffState
|
||||
${!mediaDescription && !stateObj.attributes.media_title
|
||||
? ""
|
||||
: html`
|
||||
<div class="media-info">
|
||||
<div class="title">
|
||||
<hui-marquee
|
||||
.text=${stateObj.attributes.media_title ||
|
||||
computeMediaDescription(stateObj)}
|
||||
.active=${this._marqueeActive}
|
||||
@mouseover=${this._marqueeMouseOver}
|
||||
@mouseleave=${this._marqueeMouseLeave}
|
||||
></hui-marquee>
|
||||
</div>
|
||||
<hui-marquee
|
||||
.text=${stateObj.attributes.media_title ||
|
||||
mediaDescription}
|
||||
.active=${this._marqueeActive}
|
||||
@mouseover=${this._marqueeMouseOver}
|
||||
@mouseleave=${this._marqueeMouseLeave}
|
||||
></hui-marquee>
|
||||
${!stateObj.attributes.media_title
|
||||
? ""
|
||||
: computeMediaDescription(stateObj)}
|
||||
: mediaDescription}
|
||||
</div>
|
||||
`}
|
||||
${this._veryNarrow && !isOffState
|
||||
${!showControls
|
||||
? ""
|
||||
: html`
|
||||
<div class="controls">
|
||||
<div>
|
||||
${(stateObj.state === "off" &&
|
||||
!supportsFeature(stateObj, SUPPORT_TURN_ON)) ||
|
||||
!isOffState
|
||||
? ""
|
||||
: html`
|
||||
<paper-icon-button
|
||||
icon="hass:power"
|
||||
.action=${stateObj.state === "off"
|
||||
? "turn_on"
|
||||
: "turn_off"}
|
||||
@click=${this._handleClick}
|
||||
></paper-icon-button>
|
||||
`}
|
||||
</div>
|
||||
${isOffState
|
||||
? ""
|
||||
: html`
|
||||
<div class="playback-controls">
|
||||
${!supportsFeature(
|
||||
stateObj,
|
||||
SUPPORT_PREVIOUS_TRACK
|
||||
)
|
||||
? ""
|
||||
: html`
|
||||
<paper-icon-button
|
||||
icon="hass:skip-previous"
|
||||
.action=${"media_previous_track"}
|
||||
@click=${this._handleClick}
|
||||
></paper-icon-button>
|
||||
`}
|
||||
${(stateObj.state !== "playing" &&
|
||||
!supportsFeature(
|
||||
stateObj,
|
||||
SUPPORTS_PLAY
|
||||
)) ||
|
||||
stateObj.state === UNAVAILABLE ||
|
||||
(stateObj.state === "playing" &&
|
||||
!supportsFeature(stateObj, SUPPORT_PAUSE) &&
|
||||
!supportsFeature(stateObj, SUPPORT_STOP))
|
||||
? ""
|
||||
: html`
|
||||
<paper-icon-button
|
||||
class="playPauseButton"
|
||||
.icon=${stateObj.state !== "playing"
|
||||
? "hass:play"
|
||||
: supportsFeature(
|
||||
stateObj,
|
||||
SUPPORT_PAUSE
|
||||
)
|
||||
? "hass:pause"
|
||||
: "hass:stop"}
|
||||
.action=${"media_play_pause"}
|
||||
@click=${this._handleClick}
|
||||
></paper-icon-button>
|
||||
`}
|
||||
${!supportsFeature(
|
||||
stateObj,
|
||||
SUPPORT_NEXT_TRACK
|
||||
)
|
||||
? ""
|
||||
: html`
|
||||
<paper-icon-button
|
||||
icon="hass:skip-next"
|
||||
.action=${"media_next_track"}
|
||||
@click=${this._handleClick}
|
||||
></paper-icon-button>
|
||||
`}
|
||||
</div>
|
||||
`}
|
||||
${controls!.map(
|
||||
(control) => html`
|
||||
<paper-icon-button
|
||||
.icon=${control.icon}
|
||||
action=${control.action}
|
||||
@click=${this._handleClick}
|
||||
></paper-icon-button>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`}
|
||||
</div>
|
||||
@ -346,7 +299,6 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
: html`
|
||||
<paper-progress
|
||||
.max=${stateObj.attributes.media_duration}
|
||||
class="progress"
|
||||
style=${styleMap({
|
||||
"--paper-progress-active-color":
|
||||
this._foregroundColor || "var(--accent-color)",
|
||||
@ -354,8 +306,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
? "pointer"
|
||||
: "initial",
|
||||
})}
|
||||
@click=${(e: MouseEvent) =>
|
||||
this._handleSeek(e, stateObj)}
|
||||
@click=${this._handleSeek}
|
||||
></paper-progress>
|
||||
`}
|
||||
`}
|
||||
@ -374,13 +325,20 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (!this._config || !this.hass || !changedProps.has("hass")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity] as MediaEntity;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
if (!stateObj) {
|
||||
if (this._progressInterval) {
|
||||
clearInterval(this._progressInterval);
|
||||
this._progressInterval = undefined;
|
||||
}
|
||||
this._foregroundColor = undefined;
|
||||
this._backgroundColor = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -398,13 +356,15 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
applyThemesOnElement(this, this.hass.themes, this._config.theme);
|
||||
}
|
||||
|
||||
this._updateProgressBar();
|
||||
|
||||
if (
|
||||
!this._progressInterval &&
|
||||
this._showProgressBar &&
|
||||
stateObj.state === "playing"
|
||||
) {
|
||||
this._progressInterval = window.setInterval(
|
||||
() => this._updateProgressBar(stateObj),
|
||||
() => this._updateProgressBar(),
|
||||
1000
|
||||
);
|
||||
} else if (
|
||||
@ -427,16 +387,86 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
|
||||
if (this._image !== oldImage) {
|
||||
this._setColors();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private _getControls(): ControlButton[] | undefined {
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
if (!stateObj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const state = stateObj.state;
|
||||
|
||||
if (state === UNAVAILABLE || state === UNKNOWN) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (state === "off") {
|
||||
return supportsFeature(stateObj, SUPPORT_TURN_ON)
|
||||
? [
|
||||
{
|
||||
icon: "hass:power",
|
||||
action: "turn_on",
|
||||
},
|
||||
]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
if (state === "idle") {
|
||||
return supportsFeature(stateObj, SUPPORTS_PLAY)
|
||||
? [
|
||||
{
|
||||
icon: "hass:play",
|
||||
action: "media_play",
|
||||
},
|
||||
]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const buttons: ControlButton[] = [];
|
||||
|
||||
if (supportsFeature(stateObj, SUPPORT_PREVIOUS_TRACK)) {
|
||||
buttons.push({
|
||||
icon: "hass:skip-previous",
|
||||
action: "media_previous_track",
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
(state === "playing" &&
|
||||
(supportsFeature(stateObj, SUPPORT_PAUSE) ||
|
||||
supportsFeature(stateObj, SUPPORT_STOP))) ||
|
||||
(state === "paused" && supportsFeature(stateObj, SUPPORTS_PLAY))
|
||||
) {
|
||||
buttons.push({
|
||||
icon:
|
||||
state !== "playing"
|
||||
? "hass:play"
|
||||
: supportsFeature(stateObj, SUPPORT_PAUSE)
|
||||
? "hass:pause"
|
||||
: "hass:stop",
|
||||
action: "media_play_pause",
|
||||
});
|
||||
}
|
||||
|
||||
if (supportsFeature(stateObj, SUPPORT_NEXT_TRACK)) {
|
||||
buttons.push({
|
||||
icon: "hass:skip-next",
|
||||
action: "media_next_track",
|
||||
});
|
||||
}
|
||||
|
||||
return buttons.length > 0 ? buttons : undefined;
|
||||
}
|
||||
|
||||
private get _image() {
|
||||
if (!this.hass || !this._config) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity] as MediaEntity;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
if (!stateObj) {
|
||||
return undefined;
|
||||
@ -449,21 +479,20 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
private get _showProgressBar() {
|
||||
if (!this.hass || !this._config) {
|
||||
if (!this.hass || !this._config || this._narrow) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const stateObj = this.hass.states[this._config.entity] as MediaEntity;
|
||||
const stateObj = this._stateObj;
|
||||
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
!OFF_STATES.includes(stateObj.state) &&
|
||||
stateObj.attributes.media_duration &&
|
||||
stateObj.attributes.media_position &&
|
||||
!this._narrow
|
||||
(stateObj.state === "playing" || stateObj.state === "paused") &&
|
||||
"media_duration" in stateObj.attributes &&
|
||||
"media_position" in stateObj.attributes
|
||||
);
|
||||
}
|
||||
|
||||
@ -490,7 +519,7 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
debounce(() => this._measureCard(), 250, false)
|
||||
);
|
||||
|
||||
this._resizeObserver.observe(this);
|
||||
this._resizeObserver.observe(this.shadowRoot!.querySelector("ha-card")!);
|
||||
}
|
||||
|
||||
private _handleMoreInfo(): void {
|
||||
@ -500,18 +529,28 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
private _handleClick(e: MouseEvent): void {
|
||||
this.hass!.callService("media_player", (e.currentTarget! as any).action, {
|
||||
entity_id: this._config!.entity,
|
||||
});
|
||||
this.hass!.callService(
|
||||
"media_player",
|
||||
(e.currentTarget! as PaperIconButtonElement).getAttribute("action")!,
|
||||
{
|
||||
entity_id: this._config!.entity,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _updateProgressBar(stateObj: MediaEntity): void {
|
||||
private _updateProgressBar(): void {
|
||||
if (this._progressBar) {
|
||||
this._progressBar.value = getCurrentProgress(stateObj);
|
||||
this._progressBar.value = getCurrentProgress(this._stateObj!);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleSeek(e: MouseEvent, stateObj: MediaEntity): void {
|
||||
private get _stateObj(): MediaEntity | undefined {
|
||||
return this.hass!.states[this._config!.entity] as MediaEntity;
|
||||
}
|
||||
|
||||
private _handleSeek(e: MouseEvent): void {
|
||||
const stateObj = this._stateObj!;
|
||||
|
||||
if (!supportsFeature(stateObj, SUPPORT_SEEK)) {
|
||||
return;
|
||||
}
|
||||
@ -710,9 +749,11 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.playPauseButton {
|
||||
width: 56px !important;
|
||||
height: 56px !important;
|
||||
paper-icon-button[action="media_play"],
|
||||
paper-icon-button[action="media_play_pause"],
|
||||
paper-icon-button[action="turn_on"] {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.top-info {
|
||||
@ -742,19 +783,16 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
hui-marquee {
|
||||
font-size: 1.2em;
|
||||
margin: 0px 0 4px;
|
||||
}
|
||||
|
||||
.title-controls {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.2em;
|
||||
margin: 0px 0 4px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress {
|
||||
paper-progress {
|
||||
width: 100%;
|
||||
height: var(--paper-progress-height, 4px);
|
||||
margin-top: 4px;
|
||||
@ -775,11 +813,6 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.off.player,
|
||||
.narrow.player {
|
||||
padding-bottom: 16px !important;
|
||||
}
|
||||
|
||||
.narrow .controls,
|
||||
.no-progress .controls {
|
||||
padding-bottom: 0;
|
||||
@ -790,12 +823,14 @@ export class HuiMediaControlCard extends LitElement implements LovelaceCard {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.narrow .playPauseButton {
|
||||
width: 50px !important;
|
||||
height: 50px !important;
|
||||
.narrow paper-icon-button[action="media_play"],
|
||||
.narrow paper-icon-button[action="media_play_pause"],
|
||||
.narrow paper-icon-button[action="turn_on"] {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.no-progress.player {
|
||||
.no-progress.player:not(.no-controls) {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
`;
|
||||
|
@ -8,13 +8,25 @@ import {
|
||||
CSSResult,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
|
||||
@customElement("hui-marquee")
|
||||
class HuiMarquee extends LitElement {
|
||||
@property() public text?: string;
|
||||
@property() public active?: boolean;
|
||||
@property() private _animating = false;
|
||||
@property({ type: Boolean }) public active?: boolean;
|
||||
@property({ reflect: true, type: Boolean, attribute: "animating" })
|
||||
private _animating = false;
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
|
||||
this.addEventListener("mouseover", () => this.classList.add("hovering"), {
|
||||
// Capture because we need to run before a parent sets active on us.
|
||||
// Hovering will disable the overflow, allowing us to calc if we overflow.
|
||||
capture: true,
|
||||
});
|
||||
this.addEventListener("mouseout", () => this.classList.remove("hovering"));
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
@ -33,12 +45,7 @@ class HuiMarquee extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<div
|
||||
class="marquee-inner ${classMap({
|
||||
animating: this._animating,
|
||||
})}"
|
||||
@animationiteration=${this._onIteration}
|
||||
>
|
||||
<div class="marquee-inner" @animationiteration=${this._onIteration}>
|
||||
<span>${this.text}</span>
|
||||
${this._animating
|
||||
? html`
|
||||
@ -61,15 +68,29 @@ class HuiMarquee extends LitElement {
|
||||
display: flex;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
height: 25px;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.marquee-inner {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
animation: marquee 10s linear infinite paused;
|
||||
}
|
||||
|
||||
.animating {
|
||||
:host(.hovering) .marquee-inner {
|
||||
text-overflow: initial;
|
||||
overflow: initial;
|
||||
}
|
||||
|
||||
:host([animating]) .marquee-inner {
|
||||
left: initial;
|
||||
right: initial;
|
||||
}
|
||||
|
||||
:host([animating]) > div {
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
import {
|
||||
SUPPORTS_PLAY,
|
||||
SUPPORT_NEXT_TRACK,
|
||||
OFF_STATES,
|
||||
SUPPORT_PAUSE,
|
||||
} from "../../../data/media-player";
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
@ -68,7 +67,7 @@ class HuiMediaPlayerEntityRow extends LitElement implements LovelaceRow {
|
||||
.config=${this._config}
|
||||
.secondaryText=${this._computeMediaTitle(stateObj)}
|
||||
>
|
||||
${OFF_STATES.includes(stateObj.state)
|
||||
${stateObj.state === "off" || stateObj.state === "idle"
|
||||
? html`
|
||||
<div class="text-content">
|
||||
${this.hass!.localize(`state.media_player.${stateObj.state}`) ||
|
||||
|
@ -210,6 +210,7 @@ export type MediaEntity = HassEntityBase & {
|
||||
icon?: string;
|
||||
entity_picture_local?: string;
|
||||
};
|
||||
state: "playing" | "paused" | "idle" | "off" | "unavailable" | "unknown";
|
||||
};
|
||||
|
||||
export type InputSelectEntity = HassEntityBase & {
|
||||
|
Loading…
x
Reference in New Issue
Block a user