Compare commits

...

2 Commits

Author SHA1 Message Date
Aidan Timson
a45603bf14 Pass required pro 2026-01-26 15:27:09 +00:00
Aidan Timson
22536d02a7 Migrate currency picker to generic 2026-01-26 15:24:24 +00:00
3 changed files with 79 additions and 46 deletions

View File

@@ -2,11 +2,16 @@ import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import { stopPropagation } from "../common/dom/stop_propagation";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import "./ha-list-item";
import "./ha-select";
import type { HaSelect } from "./ha-select";
import type { FrontendLocaleData } from "../data/translation";
import type { HomeAssistant, ValueChangedEvent } from "../types";
import "./ha-generic-picker";
import type { PickerComboBoxItem } from "./ha-picker-combo-box";
const SEARCH_KEYS = [
{ name: "primary", weight: 10 },
{ name: "secondary", weight: 8 },
];
const CURRENCIES = [
"AED",
@@ -172,9 +177,31 @@ const curSymbol = (currency: string, locale?: string) =>
new Intl.NumberFormat(locale, { style: "currency", currency })
.formatToParts(1)
.find((x) => x.type === "currency")?.value;
export const getCurrencyOptions = (
locale?: FrontendLocaleData
): PickerComboBoxItem[] => {
const language = locale?.language ?? "en";
const currencyDisplayNames = new Intl.DisplayNames(language, {
type: "currency",
fallback: "code",
});
const options: PickerComboBoxItem[] = CURRENCIES.map((currency) => ({
id: currency,
primary: `${currencyDisplayNames.of(currency)} (${curSymbol(currency, language)})`,
secondary: currency,
}));
options.sort((a, b) =>
caseInsensitiveStringCompare(a.primary, b.primary, language)
);
return options;
};
@customElement("ha-currency-picker")
export class HaCurrencyPicker extends LitElement {
@property() public language = "en";
@property({ attribute: false }) public hass?: HomeAssistant;
@property() public value?: string;
@@ -184,60 +211,62 @@ export class HaCurrencyPicker extends LitElement {
@property({ type: Boolean, reflect: true }) public disabled = false;
private _getOptions = memoizeOne((language?: string) => {
const currencyDisplayNames = new Intl.DisplayNames(language, {
type: "currency",
fallback: "code",
});
const options = CURRENCIES.map((currency) => ({
value: currency,
label: `${
currencyDisplayNames ? currencyDisplayNames.of(currency)! : currency
} (${curSymbol(currency, language)})`,
}));
options.sort((a, b) =>
caseInsensitiveStringCompare(a.label, b.label, language)
);
return options;
});
private _getCurrencyOptions = memoizeOne(getCurrencyOptions);
private _getItems = () => this._getCurrencyOptions(this.hass?.locale);
private _getCurrencyName = (currency?: string) =>
this._getItems().find((c) => c.id === currency)?.primary;
private _valueRenderer = (value: string) =>
html`<span slot="headline">${this._getCurrencyName(value) ?? value}</span>`;
protected render() {
const options = this._getOptions(this.language);
const label =
this.label ??
(this.hass?.localize("ui.components.currency-picker.currency") ||
"Currency");
return html`
<ha-select
.label=${this.label}
<ha-generic-picker
.hass=${this.hass}
.notFoundLabel=${this._notFoundLabel}
.emptyLabel=${this.hass?.localize(
"ui.components.currency-picker.no_currencies"
) || "No currencies available"}
.label=${label}
.value=${this.value}
.required=${this.required}
.valueRenderer=${this._valueRenderer}
.disabled=${this.disabled}
@selected=${this._changed}
@closed=${stopPropagation}
fixedMenuPosition
naturalMenuWidth
>
${options.map(
(option) => html`
<ha-list-item .value=${option.value}>${option.label}</ha-list-item>
`
)}
</ha-select>
.required=${this.required}
.getItems=${this._getItems}
.searchKeys=${SEARCH_KEYS}
@value-changed=${this._changed}
hide-clear-icon
></ha-generic-picker>
`;
}
static styles = css`
ha-select {
ha-generic-picker {
width: 100%;
min-width: 200px;
display: block;
}
`;
private _changed(ev): void {
const target = ev.target as HaSelect;
if (target.value === "" || target.value === this.value) {
return;
}
this.value = target.value;
private _changed(ev: ValueChangedEvent<string>): void {
ev.stopPropagation();
this.value = ev.detail.value;
fireEvent(this, "value-changed", { value: this.value });
}
private _notFoundLabel = (search: string) => {
const term = html`<b>'${search}'</b>`;
return this.hass
? this.hass.localize("ui.components.currency-picker.no_match", { term })
: html`No currencies found for ${term}`;
};
}
declare global {

View File

@@ -200,7 +200,7 @@ class HaConfigSectionGeneral extends LitElement {
</div>
<div>
<ha-currency-picker
.language=${this.hass.locale.language}
.hass=${this.hass}
.label=${this.hass.localize(
"ui.panel.config.core.section.core.core_config.currency"
)}
@@ -208,8 +208,7 @@ class HaConfigSectionGeneral extends LitElement {
.disabled=${disabled}
.value=${this._currency}
@value-changed=${this._handleValueChanged}
>
</ha-currency-picker>
></ha-currency-picker>
<a
href="https://en.wikipedia.org/wiki/ISO_4217#Active_codes"
target="_blank"

View File

@@ -764,6 +764,11 @@
"country-picker": {
"country": "Country"
},
"currency-picker": {
"currency": "Currency",
"no_match": "No currencies found for {term}",
"no_currencies": "No currencies available"
},
"pipeline-picker": {
"pipeline": "Assistant",
"preferred": "Preferred assistant ({preferred})",