mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 01:06:35 +00:00
♿ Add tabindex to lovelace elements (#4160)
* tabindex * use action handler * circular focus test * address comment * add focus styling to other elements * add focus styling to cards * style glance card entities * Add back light/thermo changes that were lost in rebase * Remove unused import * lint * lint * 💄 tweak focus style for glance entities * 💄 apply styling to focused state-label-badges
This commit is contained in:
parent
2848e3a63b
commit
0f487ae4bf
@ -15,6 +15,7 @@ class HaCallServiceButton extends EventsMixin(PolymerElement) {
|
||||
id="progress"
|
||||
progress="[[progress]]"
|
||||
on-click="buttonTapped"
|
||||
tabindex="0"
|
||||
><slot></slot
|
||||
></ha-progress-button>
|
||||
`;
|
||||
|
@ -4,6 +4,8 @@ import {
|
||||
TemplateResult,
|
||||
customElement,
|
||||
property,
|
||||
CSSResult,
|
||||
css,
|
||||
} from "lit-element";
|
||||
|
||||
import "../../../components/entity/ha-state-label-badge";
|
||||
@ -45,6 +47,7 @@ export class HuiStateLabelBadge extends LitElement implements LovelaceBadge {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
></ha-state-label-badge>
|
||||
`;
|
||||
}
|
||||
@ -52,6 +55,21 @@ export class HuiStateLabelBadge extends LitElement implements LovelaceBadge {
|
||||
private _handleAction(ev: ActionHandlerEvent) {
|
||||
handleAction(this, this.hass!, this._config!, ev.detail.action!);
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
ha-state-label-badge:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 4px;
|
||||
}
|
||||
ha-state-label-badge {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
margin: -4px 0 -4px 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
@ -134,6 +134,7 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
>
|
||||
${this._config.show_icon
|
||||
? html`
|
||||
@ -195,6 +196,11 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
ha-card:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
}
|
||||
|
||||
ha-icon {
|
||||
width: 40%;
|
||||
height: auto;
|
||||
|
@ -104,6 +104,7 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||
return html`
|
||||
<ha-card
|
||||
@click="${this._handleClick}"
|
||||
tabindex="0"
|
||||
style=${styleMap({
|
||||
"--base-unit": this._baseUnit,
|
||||
})}
|
||||
@ -228,6 +229,10 @@ class HuiGaugeCard extends LitElement implements LovelaceCard {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
ha-card:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
}
|
||||
.container {
|
||||
width: calc(var(--base-unit) * 4);
|
||||
height: calc(var(--base-unit) * 2);
|
||||
|
@ -167,6 +167,13 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
|
||||
margin-bottom: 12px;
|
||||
width: var(--glance-column-width, 20%);
|
||||
}
|
||||
.entity:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 14px;
|
||||
padding: 4px;
|
||||
margin: -4px 0;
|
||||
}
|
||||
.entity div {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
@ -207,6 +214,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
|
||||
hasHold: hasAction(entityConf.hold_action),
|
||||
hasDoubleClick: hasAction(entityConf.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
>
|
||||
${this._config!.show_name !== false
|
||||
? html`
|
||||
|
@ -94,7 +94,8 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
|
||||
<paper-icon-button
|
||||
icon="hass:dots-vertical"
|
||||
class="more-info"
|
||||
@click="${this._handleMoreInfo}"
|
||||
@click=${this._handleMoreInfo}
|
||||
tabindex="0"
|
||||
></paper-icon-button>
|
||||
|
||||
<div id="controls">
|
||||
@ -121,6 +122,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
|
||||
color: this._computeColor(stateObj),
|
||||
})}
|
||||
@click=${this._handleClick}
|
||||
tabindex="0"
|
||||
></paper-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -147,6 +147,7 @@ class HuiMapCard extends LitElement implements LovelaceCard {
|
||||
></div>
|
||||
<paper-icon-button
|
||||
@click=${this._fitMap}
|
||||
tabindex="0"
|
||||
icon="hass:image-filter-center-focus"
|
||||
title="Reset focus"
|
||||
></paper-icon-button>
|
||||
|
@ -86,6 +86,7 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
class="${classMap({
|
||||
clickable: Boolean(
|
||||
this._config.tap_action || this._config.hold_action
|
||||
|
@ -156,6 +156,7 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
class=${classMap({
|
||||
clickable: stateObj.state !== UNAVAILABLE,
|
||||
})}
|
||||
|
@ -168,6 +168,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
.config=${this._config}
|
||||
.hass=${this.hass}
|
||||
.image=${this._config.image}
|
||||
@ -230,6 +231,7 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
||||
hasHold: hasAction(entityConf.hold_action),
|
||||
hasDoubleClick: hasAction(entityConf.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
.config=${entityConf}
|
||||
class="${classMap({
|
||||
"state-on": !STATES_OFF.has(stateObj.state),
|
||||
@ -318,6 +320,11 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
|
||||
padding-bottom: 4px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
ha-icon:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
.state {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
|
@ -21,6 +21,7 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
import { PlantStatusCardConfig, PlantAttributeTarget } from "./types";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
|
||||
const SENSORS = {
|
||||
moisture: "hass:water",
|
||||
@ -119,7 +120,9 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard {
|
||||
(item) => html`
|
||||
<div
|
||||
class="attributes"
|
||||
@click="${this._handleMoreInfo}"
|
||||
@action=${this._handleMoreInfo}
|
||||
.actionHandler=${actionHandler()}
|
||||
tabindex="0"
|
||||
.value="${item}"
|
||||
>
|
||||
<div>
|
||||
@ -206,6 +209,12 @@ class HuiPlantStatusCard extends LitElement implements LovelaceCard {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.attributes:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.attributes div {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { fetchRecent } from "../../../data/history";
|
||||
import { SensorCardConfig } from "./types";
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
|
||||
const midPoint = (
|
||||
_Ax: number,
|
||||
@ -241,7 +242,11 @@ class HuiSensorCard extends LitElement implements LovelaceCard {
|
||||
graph = "";
|
||||
}
|
||||
return html`
|
||||
<ha-card @click="${this._handleClick}">
|
||||
<ha-card
|
||||
@action=${this._handleClick}
|
||||
.actionHandler=${actionHandler()}
|
||||
tabindex="0"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="icon">
|
||||
<ha-icon
|
||||
@ -353,6 +358,11 @@ class HuiSensorCard extends LitElement implements LovelaceCard {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ha-card:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
} from "../../../data/shopping-list";
|
||||
import { ShoppingListCardConfig, SensorCardConfig } from "./types";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
|
||||
@customElement("hui-shopping-list-card")
|
||||
class HuiShoppingListCard extends LitElement implements LovelaceCard {
|
||||
@ -165,7 +166,9 @@ class HuiShoppingListCard extends LitElement implements LovelaceCard {
|
||||
</span>
|
||||
<ha-icon
|
||||
class="clearall"
|
||||
@click="${this._clearItems}"
|
||||
@action=${this._clearItems}
|
||||
.actionHandler=${actionHandler()}
|
||||
tabindex="0"
|
||||
icon="hass:notification-clear-all"
|
||||
.title="${this.hass!.localize(
|
||||
"ui.panel.lovelace.cards.shopping-list.clear_items"
|
||||
|
@ -33,6 +33,7 @@ import {
|
||||
CLIMATE_PRESET_NONE,
|
||||
} from "../../../data/climate";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
|
||||
const modeIcons: { [mode in HvacMode]: string } = {
|
||||
auto: "hass:calendar-repeat",
|
||||
@ -218,6 +219,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
icon="hass:dots-vertical"
|
||||
class="more-info"
|
||||
@click=${this._handleMoreInfo}
|
||||
tabindex="0"
|
||||
></paper-icon-button>
|
||||
|
||||
<div id="controls">
|
||||
@ -364,9 +366,10 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
class="${classMap({ "selected-icon": currentMode === mode })}"
|
||||
.mode="${mode}"
|
||||
.icon="${modeIcons[mode]}"
|
||||
@click="${this._handleModeClick}"
|
||||
@action=${this._handleAction}
|
||||
.actionHandler=${actionHandler()}
|
||||
tabindex="0"
|
||||
></paper-icon-button>
|
||||
></ha-icon>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -376,7 +379,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleModeClick(e: MouseEvent): void {
|
||||
private _handleAction(e: MouseEvent): void {
|
||||
this.hass!.callService("climate", "set_hvac_mode", {
|
||||
entity_id: this._config!.entity,
|
||||
hvac_mode: (e.currentTarget as any).mode,
|
||||
|
@ -23,6 +23,7 @@ import { computeRTL } from "../../../common/util/compute_rtl";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { toggleAttribute } from "../../../common/dom/toggle_attribute";
|
||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||
|
||||
const cardinalDirections = [
|
||||
"N",
|
||||
@ -141,7 +142,11 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
||||
: undefined;
|
||||
|
||||
return html`
|
||||
<ha-card @click="${this.handleClick}">
|
||||
<ha-card
|
||||
@action=${this._handleAction}
|
||||
.actionHandler=${actionHandler()}
|
||||
tabindex="0"
|
||||
>
|
||||
<div class="header">
|
||||
${this.hass.localize(`state.weather.${stateObj.state}`) ||
|
||||
stateObj.state}
|
||||
@ -274,7 +279,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
|
||||
return hasConfigOrEntityChanged(this, changedProps);
|
||||
}
|
||||
|
||||
private handleClick(): void {
|
||||
private _handleAction(): void {
|
||||
fireEvent(this, "hass-more-info", { entityId: this._config!.entity });
|
||||
}
|
||||
|
||||
|
@ -171,6 +171,11 @@ class HuiGenericEntityRow extends LitElement {
|
||||
state-badge {
|
||||
flex: 0 0 40px;
|
||||
}
|
||||
state-badge:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
:host([rtl]) .flex {
|
||||
margin-left: 0;
|
||||
margin-right: 16px;
|
||||
|
@ -45,6 +45,7 @@ export class HuiIconElement extends LitElement implements LovelaceElement {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
></ha-icon>
|
||||
`;
|
||||
}
|
||||
@ -58,6 +59,11 @@ export class HuiIconElement extends LitElement implements LovelaceElement {
|
||||
:host {
|
||||
cursor: pointer;
|
||||
}
|
||||
ha-icon:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ export class HuiImageElement extends LitElement implements LovelaceElement {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
></hui-image>
|
||||
`;
|
||||
}
|
||||
@ -70,6 +71,11 @@ export class HuiImageElement extends LitElement implements LovelaceElement {
|
||||
hui-image {
|
||||
-webkit-user-select: none !important;
|
||||
}
|
||||
hui-image:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -70,6 +70,7 @@ export class HuiStateBadgeElement extends LitElement
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
></ha-state-label-badge>
|
||||
`;
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
.overrideIcon=${this._config.icon}
|
||||
></state-badge>
|
||||
`;
|
||||
@ -76,6 +77,11 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement {
|
||||
:host {
|
||||
cursor: pointer;
|
||||
}
|
||||
state-badge:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -65,6 +65,7 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
|
||||
hasHold: hasAction(this._config!.hold_action),
|
||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||
})}
|
||||
tabindex="0"
|
||||
>
|
||||
${this._config.prefix}${stateObj
|
||||
? computeStateDisplay(
|
||||
@ -90,6 +91,11 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
|
||||
padding: 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
div:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,6 @@ class HuiInputSelectEntityRow extends LitElement implements EntityRow {
|
||||
margin-left: 16px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
min-width: 200px;
|
||||
@ -149,6 +148,11 @@ class HuiInputSelectEntityRow extends LitElement implements EntityRow {
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
state-badge:focus {
|
||||
outline: none;
|
||||
background: var(--divider-color);
|
||||
border-radius: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user