Compare commits

..

7 Commits

Author SHA1 Message Date
Zack
1f6243145f Add use_uuid 2022-03-24 13:43:59 -05:00
Brynley McDonald
27ca61ec85 Fix issue where theme select does not appear when user's theme is deleted (#12104) 2022-03-24 16:31:55 +01:00
Zack Barett
859f49f3eb Update type for backend (#12122) 2022-03-24 13:47:07 +00:00
Erik Montnemery
40d878689f Sort selectors (#12120) 2022-03-24 11:53:32 +01:00
Zack Barett
420e8fe1ff Merge pull request #12116 from home-assistant/docs-only-form 2022-03-23 17:40:40 -05:00
Erik Montnemery
df96199433 Support descriptions in flow menu steps (#12108)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2022-03-23 22:14:57 +00:00
Paulus Schoutsen
cbd030a379 Only show docs link when showing a form 2022-03-23 14:53:02 -07:00
11 changed files with 219 additions and 153 deletions

View File

@@ -18,6 +18,7 @@ import "./state-badge";
interface HassEntityWithCachedName extends HassEntity {
friendly_name: string;
id: string;
}
export type HaEntityPickerEntityFilterFunc = (entityId: HassEntity) => boolean;
@@ -96,6 +97,9 @@ export class HaEntityPicker extends LitElement {
@property() public entityFilter?: HaEntityPickerEntityFilterFunc;
@property({ attribute: "item-value-path" }) public itemValuePath =
"entity_id";
@property({ type: Boolean }) public hideClearIcon = false;
@state() private _opened = false;
@@ -144,6 +148,7 @@ export class HaEntityPicker extends LitElement {
state: "",
last_changed: "",
last_updated: "",
id: "",
context: { id: "", user_id: null, parent_id: null },
friendly_name: this.hass!.localize(
"ui.components.entity.entity-picker.no_entities"
@@ -164,10 +169,15 @@ export class HaEntityPicker extends LitElement {
);
return entityIds
.map((key) => ({
...hass!.states[key],
friendly_name: computeStateName(hass!.states[key]) || key,
}))
.map((key) => {
const stateObj = hass!.states[key];
return {
...stateObj,
friendly_name: computeStateName(stateObj) || key,
id: stateObj.context.id,
};
})
.sort((entityA, entityB) =>
caseInsensitiveStringCompare(
entityA.friendly_name,
@@ -195,10 +205,15 @@ export class HaEntityPicker extends LitElement {
}
states = entityIds
.map((key) => ({
...hass!.states[key],
friendly_name: computeStateName(hass!.states[key]) || key,
}))
.map((key) => {
const stateObj = hass!.states[key];
return {
...stateObj,
friendly_name: computeStateName(stateObj) || key,
id: stateObj.context?.id,
};
})
.sort((entityA, entityB) =>
caseInsensitiveStringCompare(
entityA.friendly_name,
@@ -243,6 +258,7 @@ export class HaEntityPicker extends LitElement {
state: "",
last_changed: "",
last_updated: "",
id: "",
context: { id: "", user_id: null, parent_id: null },
friendly_name: this.hass!.localize(
"ui.components.entity.entity-picker.no_match"
@@ -295,8 +311,8 @@ export class HaEntityPicker extends LitElement {
protected render(): TemplateResult {
return html`
<ha-combo-box
item-value-path="entity_id"
item-label-path="friendly_name"
.itemValuePath=${this.itemValuePath}
.hass=${this.hass}
.value=${this._value}
.label=${this.label === undefined

View File

@@ -29,10 +29,11 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
.hass=${this.hass}
.value=${this.value}
.label=${this.label}
.includeEntities=${this.selector.entity.includeEntities}
.excludeEntities=${this.selector.entity.excludeEntities}
.includeEntities=${this.selector.entity.include_entities}
.excludeEntities=${this.selector.entity.exclude_entities}
.entityFilter=${this._filterEntities}
.disabled=${this.disabled}
.itemValuePath=${!this.selector.entity.use_uuid ? "entity_id" : "id"}
allow-custom-entity
></ha-entity-picker>`;
}
@@ -43,8 +44,8 @@ export class HaEntitySelector extends SubscribeMixin(LitElement) {
.hass=${this.hass}
.value=${this.value}
.entityFilter=${this._filterEntities}
.includeEntities=${this.selector.entity.includeEntities}
.excludeEntities=${this.selector.entity.excludeEntities}
.includeEntities=${this.selector.entity.include_entities}
.excludeEntities=${this.selector.entity.exclude_entities}
></ha-entities-picker>
`;
}

View File

@@ -84,8 +84,6 @@ export interface StatisticsMetaData {
statistic_id: string;
source: string;
name?: string | null;
has_sum: boolean;
has_mean: boolean;
}
export type StatisticsValidationResult =

View File

@@ -1,35 +1,51 @@
export type Selector =
| ActionSelector
| AddonSelector
| AreaSelector
| AttributeSelector
| EntitySelector
| BooleanSelector
| ColorRGBSelector
| ColorTempSelector
| DateSelector
| DateTimeSelector
| DeviceSelector
| DurationSelector
| AreaSelector
| TargetSelector
| EntitySelector
| IconSelector
| LocationSelector
| MediaSelector
| NumberSelector
| BooleanSelector
| TimeSelector
| ActionSelector
| StringSelector
| ObjectSelector
| SelectSelector
| IconSelector
| MediaSelector
| StringSelector
| TargetSelector
| ThemeSelector
| LocationSelector
| ColorTempSelector
| ColorRGBSelector;
| TimeSelector;
export interface EntitySelector {
entity: {
integration?: string;
domain?: string | string[];
device_class?: string;
multiple?: boolean;
includeEntities?: string[];
excludeEntities?: string[];
export interface ActionSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
action: {};
}
export interface AddonSelector {
addon: {
name?: string;
slug?: string;
};
}
export interface AreaSelector {
area: {
entity?: {
integration?: EntitySelector["entity"]["integration"];
domain?: EntitySelector["entity"]["domain"];
device_class?: EntitySelector["entity"]["device_class"];
};
device?: {
integration?: DeviceSelector["device"]["integration"];
manufacturer?: DeviceSelector["device"]["manufacturer"];
model?: DeviceSelector["device"]["model"];
};
};
}
@@ -39,11 +55,23 @@ export interface AttributeSelector {
};
}
export interface BooleanSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
boolean: {};
}
export interface ColorRGBSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
color_rgb: {};
}
export interface ColorTempSelector {
color_temp: {
min_mireds?: number;
max_mireds?: number;
};
}
export interface DateSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
date: {};
@@ -72,40 +100,50 @@ export interface DurationSelector {
duration: {};
}
export interface AddonSelector {
addon: {
name?: string;
slug?: string;
export interface EntitySelector {
entity: {
integration?: string;
domain?: string | string[];
device_class?: string;
multiple?: boolean;
use_uuid?: boolean;
include_entities?: string[];
exclude_entities?: string[];
};
}
export interface AreaSelector {
area: {
entity?: {
integration?: EntitySelector["entity"]["integration"];
domain?: EntitySelector["entity"]["domain"];
device_class?: EntitySelector["entity"]["device_class"];
};
device?: {
integration?: DeviceSelector["device"]["integration"];
manufacturer?: DeviceSelector["device"]["manufacturer"];
model?: DeviceSelector["device"]["model"];
};
export interface IconSelector {
icon: {
placeholder?: string;
fallbackPath?: string;
};
}
export interface TargetSelector {
target: {
entity?: {
integration?: EntitySelector["entity"]["integration"];
domain?: EntitySelector["entity"]["domain"];
device_class?: EntitySelector["entity"]["device_class"];
};
device?: {
integration?: DeviceSelector["device"]["integration"];
manufacturer?: DeviceSelector["device"]["manufacturer"];
model?: DeviceSelector["device"]["model"];
};
export interface LocationSelector {
location: { radius?: boolean; icon?: string };
}
export interface LocationSelectorValue {
latitude: number;
longitude: number;
radius?: number;
}
export interface MediaSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
media: {};
}
export interface MediaSelectorValue {
entity_id?: string;
media_content_id?: string;
media_content_type?: string;
metadata?: {
title?: string;
thumbnail?: string | null;
media_class?: string;
children_media_class?: string | null;
navigateIds?: { media_content_type: string; media_content_id: string }[];
};
}
@@ -119,28 +157,22 @@ export interface NumberSelector {
};
}
export interface ColorTempSelector {
color_temp: {
min_mireds?: number;
max_mireds?: number;
export interface ObjectSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
object: {};
}
export interface SelectOption {
value: string;
label: string;
}
export interface SelectSelector {
select: {
options: string[] | SelectOption[];
};
}
export interface BooleanSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
boolean: {};
}
export interface TimeSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
time: {};
}
export interface ActionSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
action: {};
}
export interface StringSelector {
text: {
multiline?: boolean;
@@ -162,58 +194,25 @@ export interface StringSelector {
};
}
export interface ObjectSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
object: {};
}
export interface SelectOption {
value: string;
label: string;
}
export interface SelectSelector {
select: {
options: string[] | SelectOption[];
export interface TargetSelector {
target: {
entity?: {
integration?: EntitySelector["entity"]["integration"];
domain?: EntitySelector["entity"]["domain"];
device_class?: EntitySelector["entity"]["device_class"];
};
device?: {
integration?: DeviceSelector["device"]["integration"];
manufacturer?: DeviceSelector["device"]["manufacturer"];
model?: DeviceSelector["device"]["model"];
};
};
}
export interface IconSelector {
icon: {
placeholder?: string;
fallbackPath?: string;
};
}
export interface ThemeSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
theme: {};
}
export interface MediaSelector {
export interface TimeSelector {
// eslint-disable-next-line @typescript-eslint/ban-types
media: {};
}
export interface LocationSelector {
location: { radius?: boolean; icon?: string };
}
export interface LocationSelectorValue {
latitude: number;
longitude: number;
radius?: number;
}
export interface MediaSelectorValue {
entity_id?: string;
media_content_id?: string;
media_content_type?: string;
metadata?: {
title?: string;
thumbnail?: string | null;
media_class?: string;
children_media_class?: string | null;
navigateIds?: { media_content_type: string; media_content_id: string }[];
};
time: {};
}

View File

@@ -238,12 +238,14 @@ class DataEntryFlowDialog extends LitElement {
""
: html`
<div class="dialog-actions">
${this._step
${["form", "menu", "external"].includes(
this._step?.type as any
)
? html`
<a
href=${documentationUrl(
this.hass,
`/integrations/${this._step.handler}`
`/integrations/${this._step!.handler}`
)}
target="_blank"
rel="noreferrer noopener"

View File

@@ -189,6 +189,18 @@ export const showConfigFlowDialog = (
);
},
renderMenuDescription(hass, step) {
const description = hass.localize(
`component.${step.handler}.config.step.${step.step_id}.description`,
step.description_placeholders
);
return description
? html`
<ha-markdown allowsvg breaks .content=${description}></ha-markdown>
`
: "";
},
renderMenuOption(hass, step, option) {
return hass.localize(
`component.${step.handler}.config.step.${step.step_id}.menu_options.${option}`,

View File

@@ -83,6 +83,11 @@ export interface FlowConfig {
renderMenuHeader(hass: HomeAssistant, step: DataEntryFlowStepMenu): string;
renderMenuDescription(
hass: HomeAssistant,
step: DataEntryFlowStepMenu
): TemplateResult | "";
renderMenuOption(
hass: HomeAssistant,
step: DataEntryFlowStepMenu,

View File

@@ -142,6 +142,22 @@ export const showOptionsFlowDialog = (
);
},
renderMenuDescription(hass, step) {
const description = hass.localize(
`component.${step.handler}.option.step.${step.step_id}.description`,
step.description_placeholders
);
return description
? html`
<ha-markdown
allowsvg
breaks
.content=${description}
></ha-markdown>
`
: "";
},
renderMenuOption(hass, step, option) {
return hass.localize(
`component.${step.handler}.options.step.${step.step_id}.menu_options.${option}`,

View File

@@ -35,8 +35,14 @@ class StepFlowMenu extends LitElement {
translations = this.step.menu_options;
}
const description = this.flowConfig.renderMenuDescription(
this.hass,
this.step
);
return html`
<h2>${this.flowConfig.renderMenuHeader(this.hass, this.step)}</h2>
${description ? html`<div class="content">${description}</div>` : ""}
<div class="options">
${options.map(
(option) => html`
@@ -69,6 +75,16 @@ class StepFlowMenu extends LitElement {
margin-top: 20px;
margin-bottom: 8px;
}
.content {
padding-bottom: 16px;
border-bottom: 1px solid var(--divider-color);
}
.content + .options {
margin-top: 8px;
}
mwc-list-item {
--mdc-list-side-padding: 24px;
}
`,
];
}

View File

@@ -117,26 +117,26 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
actions: {
title: "",
type: "overflow-menu",
template: (_info, statistic: StatisticsMetaData) =>
statistic.has_sum
? html`<ha-icon-overflow-menu
.hass=${this.hass}
.narrow=${this.narrow}
.items=${[
{
path: mdiSlopeUphill,
label: localize(
"ui.panel.developer-tools.tabs.statistics.adjust_sum"
),
action: () =>
showStatisticsAdjustSumDialog(this, {
statistic: statistic,
}),
},
]}
style="color: var(--secondary-text-color)"
></ha-icon-overflow-menu>`
: html``,
template: (
_info,
statistic: StatisticsMetaData
) => html`<ha-icon-overflow-menu
.hass=${this.hass}
.narrow=${this.narrow}
.items=${[
{
path: mdiSlopeUphill,
label: localize(
"ui.panel.developer-tools.tabs.statistics.adjust_sum"
),
action: () =>
showStatisticsAdjustSumDialog(this, {
statistic: statistic,
}),
},
]}
style="color: var(--secondary-text-color)"
></ha-icon-overflow-menu>`,
},
})
);
@@ -212,8 +212,6 @@ class HaPanelDevStatistics extends SubscribeMixin(LitElement) {
source: "",
state: this.hass.states[statisticId],
issues: issues[statisticId],
has_sum: false,
has_mean: false,
});
}
});

View File

@@ -173,6 +173,9 @@ export class HaPickThemeRow extends LitElement {
}
private _supportsModeSelection(themeName: string): boolean {
if (!(themeName in this.hass.themes.themes)) {
return false; // User's theme no longer exists
}
return "modes" in this.hass.themes.themes[themeName];
}