Improve title & accessibility label support.
This commit is contained in:
@@ -164,9 +164,10 @@ export class FrigateCardGalleryCore extends LitElement {
|
|||||||
</div>`
|
</div>`
|
||||||
: child.thumbnail
|
: child.thumbnail
|
||||||
? html`<img
|
? html`<img
|
||||||
title="${child.title}"
|
aria-label="${child.title}"
|
||||||
class="mdc-image-list__image"
|
class="mdc-image-list__image"
|
||||||
src="${child.thumbnail}"
|
src="${child.thumbnail}"
|
||||||
|
title="${child.title}"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
if (this.view) {
|
if (this.view) {
|
||||||
this.view
|
this.view
|
||||||
|
|||||||
+15
-6
@@ -1,3 +1,4 @@
|
|||||||
|
// TODO clip autoplay not working as expected
|
||||||
// TODO verify README links worked correctly (e.g. basic cameras configuration)
|
// TODO verify README links worked correctly (e.g. basic cameras configuration)
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -388,11 +389,11 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
|||||||
|
|
||||||
return html` <div class="embla__slide">
|
return html` <div class="embla__slide">
|
||||||
<frigate-card-live-provider
|
<frigate-card-live-provider
|
||||||
.title=${getCameraTitle(this.hass, cameraConfig)}
|
|
||||||
.hass=${this.hass}
|
|
||||||
.cameraConfig=${cameraConfig}
|
|
||||||
.liveConfig=${config}
|
|
||||||
?disabled=${this._isLazyLoading()}
|
?disabled=${this._isLazyLoading()}
|
||||||
|
.cameraConfig=${cameraConfig}
|
||||||
|
.label=${getCameraTitle(this.hass, cameraConfig)}
|
||||||
|
.liveConfig=${config}
|
||||||
|
.hass=${this.hass}
|
||||||
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) =>
|
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) =>
|
||||||
this._mediaShowEventHandler(slideIndex, e)}
|
this._mediaShowEventHandler(slideIndex, e)}
|
||||||
>
|
>
|
||||||
@@ -470,7 +471,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
|||||||
${ref(this._previousControlRef)}
|
${ref(this._previousControlRef)}
|
||||||
.direction=${'previous'}
|
.direction=${'previous'}
|
||||||
.controlConfig=${config.controls.next_previous}
|
.controlConfig=${config.controls.next_previous}
|
||||||
.title=${getCameraTitle(this.hass, prev)}
|
.label=${getCameraTitle(this.hass, prev)}
|
||||||
.icon=${getCameraIcon(this.hass, prev)}
|
.icon=${getCameraIcon(this.hass, prev)}
|
||||||
?disabled=${prev == null}
|
?disabled=${prev == null}
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
@@ -485,7 +486,7 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel {
|
|||||||
${ref(this._nextControlRef)}
|
${ref(this._nextControlRef)}
|
||||||
.direction=${'next'}
|
.direction=${'next'}
|
||||||
.controlConfig=${config.controls.next_previous}
|
.controlConfig=${config.controls.next_previous}
|
||||||
.title=${getCameraTitle(this.hass, next)}
|
.label=${getCameraTitle(this.hass, next)}
|
||||||
.icon=${getCameraIcon(this.hass, next)}
|
.icon=${getCameraIcon(this.hass, next)}
|
||||||
?disabled=${next == null}
|
?disabled=${next == null}
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
@@ -514,6 +515,10 @@ export class FrigateCardLiveProvider extends LitElement {
|
|||||||
@property({ attribute: true, type: Boolean })
|
@property({ attribute: true, type: Boolean })
|
||||||
public disabled = false;
|
public disabled = false;
|
||||||
|
|
||||||
|
// Label that is used for ARIA support and as tooltip.
|
||||||
|
@property({ attribute: false })
|
||||||
|
public label = "";
|
||||||
|
|
||||||
protected _getResolvedProvider(): LiveProvider {
|
protected _getResolvedProvider(): LiveProvider {
|
||||||
if (this.cameraConfig?.live_provider === 'auto') {
|
if (this.cameraConfig?.live_provider === 'auto') {
|
||||||
if (this.cameraConfig?.webrtc?.entity || this.cameraConfig?.webrtc?.url) {
|
if (this.cameraConfig?.webrtc?.entity || this.cameraConfig?.webrtc?.url) {
|
||||||
@@ -537,6 +542,10 @@ export class FrigateCardLiveProvider extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set title and ariaLabel from the provided label property.
|
||||||
|
this.title = this.label;
|
||||||
|
this.ariaLabel = this.label;
|
||||||
|
|
||||||
const provider = this._getResolvedProvider();
|
const provider = this._getResolvedProvider();
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
|||||||
+4
-10
@@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant, handleAction, hasAction } from 'custom-card-helpers';
|
import { HASSDomEvent, HomeAssistant, handleAction, hasAction } from 'custom-card-helpers';
|
||||||
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit';
|
||||||
import { customElement, property, state } from 'lit/decorators.js';
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
import { classMap } from 'lit/directives/class-map.js';
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
@@ -65,7 +65,7 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
* @param button The button configuration.
|
* @param button The button configuration.
|
||||||
*/
|
*/
|
||||||
protected _actionHandler(
|
protected _actionHandler(
|
||||||
ev: CustomEvent<{ action: string; config?: Actions }>,
|
ev: HASSDomEvent<{ action: string; config?: Actions }>,
|
||||||
config?: Actions,
|
config?: Actions,
|
||||||
): void {
|
): void {
|
||||||
if (!ev) {
|
if (!ev) {
|
||||||
@@ -152,21 +152,15 @@ export class FrigateCardMenu extends LitElement {
|
|||||||
button: true,
|
button: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Upon a safe distance from the release of HA 2021.11 these
|
|
||||||
// attributes can be removed from the <ha-icon-button>.
|
|
||||||
// - icon (replaced with the embedded <ha-icon>)
|
|
||||||
// - title (replaced with .label)
|
|
||||||
return html` <ha-icon-button
|
return html` <ha-icon-button
|
||||||
class="${classMap(classes)}"
|
class="${classMap(classes)}"
|
||||||
style="${styleMap(stateParameters.style || {})}"
|
style="${styleMap(stateParameters.style || {})}"
|
||||||
icon=${stateParameters.icon || 'mdi:gesture-tap-button'}
|
|
||||||
.label=${stateParameters.title || ''}
|
|
||||||
title=${stateParameters.title || ''}
|
|
||||||
@action=${(ev) => this._actionHandler(ev, button)}
|
|
||||||
.actionHandler=${actionHandler({
|
.actionHandler=${actionHandler({
|
||||||
hasHold: hasHold,
|
hasHold: hasHold,
|
||||||
hasDoubleClick: hasDoubleClick,
|
hasDoubleClick: hasDoubleClick,
|
||||||
})}
|
})}
|
||||||
|
.label=${stateParameters.title || ''}
|
||||||
|
@action=${(ev) => this._actionHandler(ev, button)}
|
||||||
>
|
>
|
||||||
<ha-icon icon="${stateParameters.icon || 'mdi:gesture-tap-button'}"></ha-icon>
|
<ha-icon icon="${stateParameters.icon || 'mdi:gesture-tap-button'}"></ha-icon>
|
||||||
</ha-icon-button>`;
|
</ha-icon-button>`;
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ export class FrigateCardNextPreviousControl extends LitElement {
|
|||||||
@property({ attribute: true, type: Boolean })
|
@property({ attribute: true, type: Boolean })
|
||||||
public disabled = false;
|
public disabled = false;
|
||||||
|
|
||||||
|
// Label that is used for ARIA support and as tooltip.
|
||||||
|
@property() label = "";
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (this.disabled || !this._controlConfig || this._controlConfig.style == 'none') {
|
if (this.disabled || !this._controlConfig || this._controlConfig.style == 'none') {
|
||||||
return html``;
|
return html``;
|
||||||
@@ -55,15 +58,9 @@ export class FrigateCardNextPreviousControl extends LitElement {
|
|||||||
icon = this.icon
|
icon = this.icon
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Upon a safe distance from the release of HA 2021.11 these
|
|
||||||
// attributes can be removed from the <ha-icon-button>.
|
|
||||||
// - icon (replaced with the embedded <ha-icon>)
|
|
||||||
// - title (replaced with .label)
|
|
||||||
return html` <ha-icon-button
|
return html` <ha-icon-button
|
||||||
icon=${icon}
|
|
||||||
class="${classMap(classes)}"
|
class="${classMap(classes)}"
|
||||||
.label=${this.title}
|
.label=${this.label}
|
||||||
title=${this.title}
|
|
||||||
>
|
>
|
||||||
<ha-icon icon=${icon}></ha-icon>
|
<ha-icon icon=${icon}></ha-icon>
|
||||||
</ha-icon-button>`;
|
</ha-icon-button>`;
|
||||||
@@ -75,7 +72,8 @@ export class FrigateCardNextPreviousControl extends LitElement {
|
|||||||
return html`<img
|
return html`<img
|
||||||
src="${this.thumbnail}"
|
src="${this.thumbnail}"
|
||||||
class="${classMap(classes)}"
|
class="${classMap(classes)}"
|
||||||
title="${this.title}"
|
title="${this.label}"
|
||||||
|
aria-label="${this.label}"
|
||||||
/>`;
|
/>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
|||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
set config(config: ThumbnailsControlConfig | undefined) {
|
set config(config: ThumbnailsControlConfig | undefined) {
|
||||||
if (config) {
|
if (config) {
|
||||||
if (config && (config.size !== undefined && config.size !== null)) {
|
if (config && config.size !== undefined && config.size !== null) {
|
||||||
this.style.setProperty('--frigate-card-carousel-thumbnail-size', config.size);
|
this.style.setProperty('--frigate-card-carousel-thumbnail-size', config.size);
|
||||||
}
|
}
|
||||||
this._config = config;
|
this._config = config;
|
||||||
@@ -35,7 +35,10 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
|||||||
|
|
||||||
@property({ attribute: false })
|
@property({ attribute: false })
|
||||||
set highlightSelected(value: boolean) {
|
set highlightSelected(value: boolean) {
|
||||||
this.style.setProperty('--frigate-card-carousel-thumbnail-opacity', value ? '0.6' : '1.0');
|
this.style.setProperty(
|
||||||
|
'--frigate-card-carousel-thumbnail-opacity',
|
||||||
|
value ? '0.6' : '1.0',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,10 +82,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
|||||||
|
|
||||||
const slides: TemplateResult[] = [];
|
const slides: TemplateResult[] = [];
|
||||||
for (let i = 0; i < this.target.children.length; ++i) {
|
for (let i = 0; i < this.target.children.length; ++i) {
|
||||||
const thumbnail = this._renderThumbnail(
|
const thumbnail = this._renderThumbnail(this.target, i, slides.length);
|
||||||
this.target,
|
|
||||||
i,
|
|
||||||
slides.length);
|
|
||||||
if (thumbnail) {
|
if (thumbnail) {
|
||||||
slides.push(thumbnail);
|
slides.push(thumbnail);
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,11 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img src="${mediaToRender.thumbnail}" title="${mediaToRender.title}" />
|
<img
|
||||||
|
aria-label="${mediaToRender.title}"
|
||||||
|
src="${mediaToRender.thumbnail}"
|
||||||
|
title="${mediaToRender.title}"
|
||||||
|
/>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -601,7 +601,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
.direction=${'previous'}
|
.direction=${'previous'}
|
||||||
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||||
.thumbnail=${prev && prev.thumbnail ? prev.thumbnail : undefined}
|
.thumbnail=${prev && prev.thumbnail ? prev.thumbnail : undefined}
|
||||||
.title=${prev ? prev.title : ''}
|
.label=${prev ? prev.title : ''}
|
||||||
?disabled=${!prev}
|
?disabled=${!prev}
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this._nextPreviousHandler('previous');
|
this._nextPreviousHandler('previous');
|
||||||
@@ -615,7 +615,7 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
.direction=${'next'}
|
.direction=${'next'}
|
||||||
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
.controlConfig=${this.viewerConfig?.controls.next_previous}
|
||||||
.thumbnail=${next && next.thumbnail ? next.thumbnail : undefined}
|
.thumbnail=${next && next.thumbnail ? next.thumbnail : undefined}
|
||||||
.title=${next ? next.title : ''}
|
.label=${next ? next.title : ''}
|
||||||
?disabled=${!next}
|
?disabled=${!next}
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
this._nextPreviousHandler('next');
|
this._nextPreviousHandler('next');
|
||||||
@@ -657,19 +657,21 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
|||||||
<div class="embla__slide">
|
<div class="embla__slide">
|
||||||
${this.view.isClipRelatedView()
|
${this.view.isClipRelatedView()
|
||||||
? html`<frigate-card-ha-hls-player
|
? html`<frigate-card-ha-hls-player
|
||||||
.hass=${this.hass}
|
|
||||||
url=${ifDefined(lazyLoad ? undefined : resolvedMedia?.url)}
|
|
||||||
title="${mediaToRender.title}"
|
|
||||||
muted
|
|
||||||
controls
|
|
||||||
playsinline
|
|
||||||
allow-exoplayer
|
allow-exoplayer
|
||||||
?autoplay="${autoplay}"
|
?autoplay="${autoplay}"
|
||||||
|
aria-label="${mediaToRender.title}"
|
||||||
|
controls
|
||||||
|
muted
|
||||||
|
playsinline
|
||||||
|
title="${mediaToRender.title}"
|
||||||
|
url=${ifDefined(lazyLoad ? undefined : resolvedMedia?.url)}
|
||||||
|
.hass=${this.hass}
|
||||||
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) =>
|
@frigate-card:media-show=${(e: CustomEvent<MediaShowInfo>) =>
|
||||||
this._mediaShowEventHandler(slideIndex, e)}
|
this._mediaShowEventHandler(slideIndex, e)}
|
||||||
>
|
>
|
||||||
</frigate-card-ha-hls-player>`
|
</frigate-card-ha-hls-player>`
|
||||||
: html`<img
|
: html`<img
|
||||||
|
aria-label="${mediaToRender.title}"
|
||||||
src=${ifDefined(lazyLoad ? IMG_EMPTY : resolvedMedia?.url)}
|
src=${ifDefined(lazyLoad ? IMG_EMPTY : resolvedMedia?.url)}
|
||||||
title="${mediaToRender.title}"
|
title="${mediaToRender.title}"
|
||||||
@click=${() => {
|
@click=${() => {
|
||||||
|
|||||||
@@ -48,10 +48,6 @@ customElements.whenDefined('ha-camera-stream').then(() => {
|
|||||||
? html`
|
? html`
|
||||||
<img
|
<img
|
||||||
@load=${(e) => {
|
@load=${(e) => {
|
||||||
// TODO: This block can be removed a safe distance from HA 2021.11.
|
|
||||||
if (typeof this._elementResized != 'undefined') {
|
|
||||||
this._elementResized();
|
|
||||||
}
|
|
||||||
dispatchMediaShowEvent(this, e);
|
dispatchMediaShowEvent(this, e);
|
||||||
}}
|
}}
|
||||||
.src=${typeof this._connected == 'undefined' || this._connected
|
.src=${typeof this._connected == 'undefined' || this._connected
|
||||||
|
|||||||
@@ -34,10 +34,6 @@ customElements.whenDefined('ha-hls-player').then(() => {
|
|||||||
?playsinline=${this.playsInline}
|
?playsinline=${this.playsInline}
|
||||||
?controls=${this.controls}
|
?controls=${this.controls}
|
||||||
@loadeddata=${(e) => {
|
@loadeddata=${(e) => {
|
||||||
// TODO: This block can be removed a safe distance from HA 2021.11.
|
|
||||||
if (typeof this._elementResized != 'undefined') {
|
|
||||||
this._elementResized();
|
|
||||||
}
|
|
||||||
dispatchMediaShowEvent(this, e);
|
dispatchMediaShowEvent(this, e);
|
||||||
}}
|
}}
|
||||||
@pause=${() => dispatchPauseEvent(this)}
|
@pause=${() => dispatchPauseEvent(this)}
|
||||||
|
|||||||
Reference in New Issue
Block a user