mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-17 22:36:35 +00:00
ha-form-multi_select accessibility improvements (#21023)
This commit is contained in:
parent
d4cbfd9583
commit
147098f0fd
@ -5,12 +5,14 @@ import { customElement, property, query, state } from "lit/decorators";
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../ha-button-menu";
|
import "../ha-button-menu";
|
||||||
import "../ha-check-list-item";
|
import "../ha-check-list-item";
|
||||||
import type { HaCheckListItem } from "../ha-check-list-item";
|
|
||||||
import "../ha-checkbox";
|
import "../ha-checkbox";
|
||||||
import type { HaCheckbox } from "../ha-checkbox";
|
import type { HaCheckbox } from "../ha-checkbox";
|
||||||
import "../ha-formfield";
|
import "../ha-formfield";
|
||||||
import "../ha-svg-icon";
|
import "../ha-icon-button";
|
||||||
import "../ha-textfield";
|
import "../ha-textfield";
|
||||||
|
import "../ha-md-button-menu";
|
||||||
|
import "../ha-md-menu-item";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
HaFormElement,
|
HaFormElement,
|
||||||
HaFormMultiSelectData,
|
HaFormMultiSelectData,
|
||||||
@ -73,13 +75,10 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-button-menu
|
<ha-md-button-menu
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
fixed
|
@opening=${this._handleOpen}
|
||||||
@opened=${this._handleOpen}
|
@closing=${this._handleClose}
|
||||||
@closed=${this._handleClose}
|
|
||||||
multi
|
|
||||||
activatable
|
|
||||||
>
|
>
|
||||||
<ha-textfield
|
<ha-textfield
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
@ -94,28 +93,56 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
></ha-textfield>
|
></ha-textfield>
|
||||||
<ha-svg-icon
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
|
.label=${this.label}
|
||||||
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
||||||
></ha-svg-icon>
|
></ha-icon-button>
|
||||||
${options.map((item: string | [string, string]) => {
|
${options.map((item: string | [string, string]) => {
|
||||||
const value = optionValue(item);
|
const value = optionValue(item);
|
||||||
const selected = data.includes(value);
|
const selected = data.includes(value);
|
||||||
return html`<ha-check-list-item
|
return html`<ha-md-menu-item
|
||||||
left
|
type="option"
|
||||||
.selected=${selected}
|
aria-checked=${selected}
|
||||||
.activated=${selected}
|
|
||||||
@request-selected=${this._selectedChanged}
|
|
||||||
.value=${value}
|
.value=${value}
|
||||||
.disabled=${this.disabled}
|
.action=${selected ? "remove" : "add"}
|
||||||
|
.activated=${selected}
|
||||||
|
@click=${this._toggleItem}
|
||||||
|
@keydown=${this._keydown}
|
||||||
|
keep-open
|
||||||
>
|
>
|
||||||
|
<ha-checkbox
|
||||||
|
slot="start"
|
||||||
|
tabindex="-1"
|
||||||
|
.checked=${selected}
|
||||||
|
></ha-checkbox>
|
||||||
${optionLabel(item)}
|
${optionLabel(item)}
|
||||||
</ha-check-list-item>`;
|
</ha-md-menu-item>`;
|
||||||
})}
|
})}
|
||||||
</ha-button-menu>
|
</ha-md-button-menu>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _keydown(ev) {
|
||||||
|
if (ev.code === "Space" || ev.code === "Enter") {
|
||||||
|
ev.preventDefault();
|
||||||
|
this._toggleItem(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _toggleItem(ev) {
|
||||||
|
const oldData = this.data || [];
|
||||||
|
let newData: string[];
|
||||||
|
if (ev.currentTarget.action === "add") {
|
||||||
|
newData = [...oldData, ev.currentTarget.value];
|
||||||
|
} else {
|
||||||
|
newData = oldData.filter((d) => d !== ev.currentTarget.value);
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: newData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
this.updateComplete.then(() => {
|
this.updateComplete.then(() => {
|
||||||
const { formElement, mdcRoot } =
|
const { formElement, mdcRoot } =
|
||||||
@ -139,17 +166,6 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _selectedChanged(ev: CustomEvent): void {
|
|
||||||
ev.stopPropagation();
|
|
||||||
if (ev.detail.source === "property") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._handleValueChanged(
|
|
||||||
(ev.target as HaCheckListItem).value,
|
|
||||||
ev.detail.selected
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _valueChanged(ev: CustomEvent): void {
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
const { value, checked } = ev.target as HaCheckbox;
|
const { value, checked } = ev.target as HaCheckbox;
|
||||||
this._handleValueChanged(value, checked);
|
this._handleValueChanged(value, checked);
|
||||||
@ -195,7 +211,7 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
|||||||
:host([own-margin]) {
|
:host([own-margin]) {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
ha-button-menu {
|
ha-md-button-menu {
|
||||||
display: block;
|
display: block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -208,22 +224,23 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
|||||||
}
|
}
|
||||||
ha-textfield {
|
ha-textfield {
|
||||||
display: block;
|
display: block;
|
||||||
|
width: 100%;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
ha-svg-icon {
|
ha-icon-button {
|
||||||
color: var(--input-dropdown-icon-color);
|
color: var(--input-dropdown-icon-color);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 1em;
|
right: 1em;
|
||||||
top: 1em;
|
top: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
inset-inline-end: 1em;
|
inset-inline-end: 1em;
|
||||||
inset-inline-start: initial;
|
inset-inline-start: initial;
|
||||||
direction: var(--direction);
|
direction: var(--direction);
|
||||||
}
|
}
|
||||||
:host([opened]) ha-svg-icon {
|
:host([opened]) ha-icon-button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
:host([opened]) ha-button-menu {
|
:host([opened]) ha-md-button-menu {
|
||||||
--mdc-text-field-idle-line-color: var(--input-hover-line-color);
|
--mdc-text-field-idle-line-color: var(--input-hover-line-color);
|
||||||
--mdc-text-field-label-ink-color: var(--primary-color);
|
--mdc-text-field-label-ink-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import type { CSSResultGroup, TemplateResult } from "lit";
|
|||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, query } from "lit/decorators";
|
import { customElement, property, query } from "lit/decorators";
|
||||||
import { FOCUS_TARGET } from "../dialogs/make-dialog-manager";
|
import { FOCUS_TARGET } from "../dialogs/make-dialog-manager";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import type { HaIconButton } from "./ha-icon-button";
|
import type { HaIconButton } from "./ha-icon-button";
|
||||||
import "./ha-menu";
|
import "./ha-menu";
|
||||||
import type { HaMenu } from "./ha-menu";
|
import type { HaMenu } from "./ha-menu";
|
||||||
@ -40,12 +41,22 @@ export class HaMdButtonMenu extends LitElement {
|
|||||||
<ha-menu
|
<ha-menu
|
||||||
.positioning=${this.positioning}
|
.positioning=${this.positioning}
|
||||||
.hasOverflow=${this.hasOverflow}
|
.hasOverflow=${this.hasOverflow}
|
||||||
|
@opening=${this._handleOpening}
|
||||||
|
@closing=${this._handleClosing}
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</ha-menu>
|
</ha-menu>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleOpening(): void {
|
||||||
|
fireEvent(this, "opening", undefined, { composed: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleClosing(): void {
|
||||||
|
fireEvent(this, "closing", undefined, { composed: false });
|
||||||
|
}
|
||||||
|
|
||||||
private _handleClick(): void {
|
private _handleClick(): void {
|
||||||
if (this.disabled) {
|
if (this.disabled) {
|
||||||
return;
|
return;
|
||||||
@ -88,3 +99,10 @@ declare global {
|
|||||||
"ha-md-button-menu": HaMdButtonMenu;
|
"ha-md-button-menu": HaMdButtonMenu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
opening: undefined;
|
||||||
|
closing: undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user