mirror of
https://github.com/home-assistant/frontend.git
synced 2026-02-04 01:10:33 -06:00
Replace ha-md-button-menu with ha-dropdown (#29210)
This commit is contained in:
parent
6e1999ceb7
commit
cddf91cfd0
@ -1,17 +1,18 @@
|
||||
import { mdiMenuDown, mdiMenuUp } from "@mdi/js";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import "../ha-check-list-item";
|
||||
import "../ha-checkbox";
|
||||
import type { HaCheckbox } from "../ha-checkbox";
|
||||
import "../ha-dropdown";
|
||||
import "../ha-dropdown-item";
|
||||
import "../ha-formfield";
|
||||
import "../ha-icon-button";
|
||||
import "../ha-md-button-menu";
|
||||
import "../ha-md-menu-item";
|
||||
import "../ha-textfield";
|
||||
import "../ha-picker-field";
|
||||
|
||||
import type { HaDropdown } from "../ha-dropdown";
|
||||
import type { HaDropdownItem } from "../ha-dropdown-item";
|
||||
import type {
|
||||
HaFormElement,
|
||||
HaFormMultiSelectData,
|
||||
@ -36,16 +37,12 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
||||
|
||||
@property() public label!: string;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||
|
||||
@state() private _opened = false;
|
||||
|
||||
@query("ha-md-button-menu") private _input?: HTMLElement;
|
||||
@query("ha-dropdown") private _dropdown!: HaDropdown;
|
||||
|
||||
public focus(): void {
|
||||
if (this._input) {
|
||||
this._input.focus();
|
||||
}
|
||||
this._dropdown?.focus();
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
@ -74,13 +71,14 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-md-button-menu
|
||||
.disabled=${this.disabled}
|
||||
@opening=${this._handleOpen}
|
||||
@closing=${this._handleClose}
|
||||
positioning="fixed"
|
||||
<ha-dropdown
|
||||
@wa-select=${this._toggleItem}
|
||||
@wa-show=${this._showDropdown}
|
||||
placement="bottom"
|
||||
tabindex="0"
|
||||
@keydown=${this._handleKeydown}
|
||||
>
|
||||
<ha-textfield
|
||||
<ha-picker-field
|
||||
slot="trigger"
|
||||
.label=${this.label}
|
||||
.value=${data
|
||||
@ -91,71 +89,42 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
||||
)
|
||||
.join(", ")}
|
||||
.disabled=${this.disabled}
|
||||
tabindex="-1"
|
||||
></ha-textfield>
|
||||
<ha-icon-button
|
||||
slot="trigger"
|
||||
.label=${this.label}
|
||||
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
||||
></ha-icon-button>
|
||||
hide-clear-icon
|
||||
>
|
||||
</ha-picker-field>
|
||||
${options.map((item: string | [string, string]) => {
|
||||
const value = optionValue(item);
|
||||
const selected = data.includes(value);
|
||||
return html`<ha-md-menu-item
|
||||
type="option"
|
||||
aria-checked=${selected}
|
||||
return html`<ha-dropdown-item
|
||||
.value=${value}
|
||||
.action=${selected ? "remove" : "add"}
|
||||
.activated=${selected}
|
||||
@click=${this._toggleItem}
|
||||
@keydown=${this._keydown}
|
||||
keep-open
|
||||
type="checkbox"
|
||||
.checked=${selected}
|
||||
>
|
||||
<ha-checkbox
|
||||
slot="start"
|
||||
tabindex="-1"
|
||||
.checked=${selected}
|
||||
></ha-checkbox>
|
||||
${optionLabel(item)}
|
||||
</ha-md-menu-item>`;
|
||||
</ha-dropdown-item>`;
|
||||
})}
|
||||
</ha-md-button-menu>
|
||||
</ha-dropdown>
|
||||
`;
|
||||
}
|
||||
|
||||
protected _keydown(ev) {
|
||||
if (ev.code === "Space" || ev.code === "Enter") {
|
||||
ev.preventDefault();
|
||||
this._toggleItem(ev);
|
||||
}
|
||||
}
|
||||
protected _toggleItem(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
||||
ev.preventDefault(); // keep the dropdown open
|
||||
const value = ev.detail.item.value;
|
||||
const action = (ev.detail.item as any).action;
|
||||
|
||||
protected _toggleItem(ev) {
|
||||
const oldData = this.data || [];
|
||||
let newData: string[];
|
||||
if (ev.currentTarget.action === "add") {
|
||||
newData = [...oldData, ev.currentTarget.value];
|
||||
if (action === "add") {
|
||||
newData = [...oldData, value];
|
||||
} else {
|
||||
newData = oldData.filter((d) => d !== ev.currentTarget.value);
|
||||
newData = oldData.filter((d) => d !== value);
|
||||
}
|
||||
fireEvent(this, "value-changed", {
|
||||
value: newData,
|
||||
});
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
this.updateComplete.then(() => {
|
||||
const { formElement, mdcRoot } =
|
||||
this.shadowRoot?.querySelector("ha-textfield") || ({} as any);
|
||||
if (formElement) {
|
||||
formElement.style.textOverflow = "ellipsis";
|
||||
}
|
||||
if (mdcRoot) {
|
||||
mdcRoot.style.cursor = "pointer";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
if (changedProps.has("schema")) {
|
||||
this.toggleAttribute(
|
||||
@ -194,25 +163,28 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleOpen(ev: Event): void {
|
||||
ev.stopPropagation();
|
||||
this._opened = true;
|
||||
this.toggleAttribute("opened", true);
|
||||
private _showDropdown(ev) {
|
||||
if (this.disabled) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
this.style.setProperty(
|
||||
"--dropdown-width",
|
||||
`${this._dropdown.offsetWidth}px`
|
||||
);
|
||||
}
|
||||
|
||||
private _handleClose(ev: Event): void {
|
||||
ev.stopPropagation();
|
||||
this._opened = false;
|
||||
this.toggleAttribute("opened", false);
|
||||
private _handleKeydown(ev) {
|
||||
if ((ev.code === "Space" || ev.code === "Enter") && this._dropdown) {
|
||||
this._dropdown.open = true;
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host([own-margin]) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
ha-md-button-menu {
|
||||
ha-dropdown {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
ha-formfield {
|
||||
display: block;
|
||||
@ -239,9 +211,15 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
|
||||
:host([opened]) ha-icon-button {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
:host([opened]) ha-md-button-menu {
|
||||
--mdc-text-field-idle-line-color: var(--input-hover-line-color);
|
||||
--mdc-text-field-label-ink-color: var(--primary-color);
|
||||
|
||||
ha-dropdown::part(menu) {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
width: var(--dropdown-width);
|
||||
}
|
||||
|
||||
:host([disabled]) ha-dropdown ha-picker-field {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@ -1,123 +0,0 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { FOCUS_TARGET } from "../dialogs/make-dialog-manager";
|
||||
import type { HaButton } from "./ha-button";
|
||||
import type { HaIconButton } from "./ha-icon-button";
|
||||
import "./ha-md-menu";
|
||||
import type { HaMdMenu } from "./ha-md-menu";
|
||||
|
||||
@customElement("ha-md-button-menu")
|
||||
export class HaMdButtonMenu extends LitElement {
|
||||
protected readonly [FOCUS_TARGET];
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property() public positioning?: "fixed" | "absolute" | "popover";
|
||||
|
||||
@property({ attribute: "anchor-corner" }) public anchorCorner:
|
||||
| "start-start"
|
||||
| "start-end"
|
||||
| "end-start"
|
||||
| "end-end" = "end-start";
|
||||
|
||||
@property({ attribute: "menu-corner" }) public menuCorner:
|
||||
| "start-start"
|
||||
| "start-end"
|
||||
| "end-start"
|
||||
| "end-end" = "start-start";
|
||||
|
||||
@property({ type: Boolean, attribute: "has-overflow" }) public hasOverflow =
|
||||
false;
|
||||
|
||||
@property({ type: Boolean }) public quick = false;
|
||||
|
||||
@query("ha-md-menu", true) private _menu!: HaMdMenu;
|
||||
|
||||
public get items() {
|
||||
return this._menu.items;
|
||||
}
|
||||
|
||||
public override focus() {
|
||||
if (this._menu.open) {
|
||||
this._menu.focus();
|
||||
} else {
|
||||
this._triggerButton?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div @click=${this._handleClick}>
|
||||
<slot name="trigger" @slotchange=${this._setTriggerAria}></slot>
|
||||
</div>
|
||||
<ha-md-menu
|
||||
.quick=${this.quick}
|
||||
.positioning=${this.positioning}
|
||||
.hasOverflow=${this.hasOverflow}
|
||||
.anchorCorner=${this.anchorCorner}
|
||||
.menuCorner=${this.menuCorner}
|
||||
@opening=${this._handleOpening}
|
||||
@closing=${this._handleClosing}
|
||||
>
|
||||
<slot></slot>
|
||||
</ha-md-menu>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleOpening(): void {
|
||||
fireEvent(this, "opening", undefined, { composed: false });
|
||||
}
|
||||
|
||||
private _handleClosing(): void {
|
||||
fireEvent(this, "closing", undefined, { composed: false });
|
||||
}
|
||||
|
||||
private _handleClick(): void {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
this._menu.anchorElement = this;
|
||||
if (this._menu.open) {
|
||||
this._menu.close();
|
||||
} else {
|
||||
this._menu.show();
|
||||
}
|
||||
}
|
||||
|
||||
private get _triggerButton() {
|
||||
return this.querySelector(
|
||||
'ha-icon-button[slot="trigger"], ha-button[slot="trigger"], ha-assist-chip[slot="trigger"]'
|
||||
) as HaIconButton | HaButton | null;
|
||||
}
|
||||
|
||||
private _setTriggerAria() {
|
||||
if (this._triggerButton) {
|
||||
this._triggerButton.ariaHasPopup = "menu";
|
||||
}
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
::slotted([disabled]) {
|
||||
color: var(--disabled-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-md-button-menu": HaMdButtonMenu;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
opening: undefined;
|
||||
closing: undefined;
|
||||
}
|
||||
}
|
||||
@ -8,8 +8,10 @@ import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { formatLanguageCode } from "../../common/language/format_language";
|
||||
import "../../components/chips/ha-assist-chip";
|
||||
import "../../components/ha-dialog";
|
||||
import "../../components/ha-dropdown";
|
||||
import "../../components/ha-dropdown-item";
|
||||
import type { HaDropdownItem } from "../../components/ha-dropdown-item";
|
||||
import { getLanguageOptions } from "../../components/ha-language-picker";
|
||||
import "../../components/ha-md-button-menu";
|
||||
import type { AssistSatelliteConfiguration } from "../../data/assist_satellite";
|
||||
import { fetchAssistSatelliteConfiguration } from "../../data/assist_satellite";
|
||||
import { getLanguageScores } from "../../data/conversation";
|
||||
@ -169,9 +171,9 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
|
||||
>`
|
||||
: this._step === STEP.PIPELINE
|
||||
? this._language
|
||||
? html`<ha-md-button-menu
|
||||
? html`<ha-dropdown
|
||||
slot="actionItems"
|
||||
positioning="fixed"
|
||||
@wa-select=${this._handlePickLanguage}
|
||||
>
|
||||
<ha-assist-chip
|
||||
.label=${formatLanguageCode(
|
||||
@ -192,16 +194,14 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
|
||||
this.hass.locale
|
||||
).map(
|
||||
(lang) =>
|
||||
html`<ha-md-menu-item
|
||||
html`<ha-dropdown-item
|
||||
.value=${lang.id}
|
||||
@click=${this._handlePickLanguage}
|
||||
@keydown=${this._handlePickLanguage}
|
||||
.selected=${this._language === lang.id}
|
||||
class=${this._language === lang.id ? "selected" : ""}
|
||||
>
|
||||
${lang.primary}
|
||||
</ha-md-menu-item>`
|
||||
</ha-dropdown-item>`
|
||||
)}
|
||||
</ha-md-button-menu>`
|
||||
</ha-dropdown>`
|
||||
: nothing
|
||||
: nothing}
|
||||
</ha-dialog-header>
|
||||
@ -328,10 +328,8 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _handlePickLanguage(ev) {
|
||||
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") return;
|
||||
|
||||
this._language = ev.target.value;
|
||||
private _handlePickLanguage(ev: CustomEvent<{ item: HaDropdownItem }>) {
|
||||
this._language = ev.detail.item.value;
|
||||
}
|
||||
|
||||
private _languageChanged(ev: CustomEvent) {
|
||||
@ -401,7 +399,7 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
|
||||
margin: 24px;
|
||||
display: block;
|
||||
}
|
||||
ha-md-button-menu {
|
||||
ha-dropdown {
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -409,6 +407,13 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
|
||||
margin-inline-end: 12px;
|
||||
margin-inline-start: initial;
|
||||
}
|
||||
ha-dropdown-item.selected {
|
||||
border: 1px solid var(--primary-color);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
color: var(--primary-color);
|
||||
background-color: var(--ha-color-fill-primary-quiet-resting);
|
||||
--icon-primary-color: var(--primary-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { firstWeekdayIndex } from "../../../../../common/datetime/first_weekday";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../../../../common/entity/compute_domain";
|
||||
import type { LocalizeFunc } from "../../../../../common/translations/localize";
|
||||
import "../../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../../components/ha-form/types";
|
||||
@ -11,7 +12,6 @@ import type { TimeTrigger } from "../../../../../data/automation";
|
||||
import type { FrontendLocaleData } from "../../../../../data/translation";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import type { TriggerElement } from "../ha-automation-trigger-row";
|
||||
import { computeDomain } from "../../../../../common/entity/compute_domain";
|
||||
|
||||
const MODE_TIME = "time";
|
||||
const MODE_ENTITY = "entity";
|
||||
|
||||
@ -20,7 +20,6 @@ import "../../components/ha-dropdown-item";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-label";
|
||||
import "../../components/ha-list-item";
|
||||
import "../../components/ha-md-button-menu";
|
||||
import "../../components/ha-md-menu-item";
|
||||
import "../../components/ha-settings-row";
|
||||
import { deleteAllRefreshTokens } from "../../data/auth";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user