Column span better editor (#21820)

* Add label, unit and max for column span option

* Display the right number of columns in the layout editor

* improve translations
This commit is contained in:
Paul Bottein 2024-08-28 14:58:46 +02:00 committed by GitHub
parent 8b5f731d0c
commit c556742ff4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 89 additions and 54 deletions

View File

@ -213,7 +213,7 @@ export class HaGridSizeEditor extends LitElement {
grid-template-areas: grid-template-areas:
"reset column-slider" "reset column-slider"
"row-slider preview"; "row-slider preview";
grid-template-rows: auto 1fr; grid-template-rows: auto auto;
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
gap: 8px; gap: 8px;
} }
@ -229,17 +229,12 @@ export class HaGridSizeEditor extends LitElement {
.preview { .preview {
position: relative; position: relative;
grid-area: preview; grid-area: preview;
aspect-ratio: 1 / 1.2;
} }
.preview > div { .preview > div {
position: absolute; position: relative;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: grid; display: grid;
grid-template-columns: repeat(var(--total-columns), 1fr); grid-template-columns: repeat(var(--total-columns), 1fr);
grid-template-rows: repeat(var(--total-rows), 1fr); grid-template-rows: repeat(var(--total-rows), 25px);
gap: 4px; gap: 4px;
} }
.preview .cell { .preview .cell {
@ -250,8 +245,13 @@ export class HaGridSizeEditor extends LitElement {
opacity: 0.2; opacity: 0.2;
cursor: pointer; cursor: pointer;
} }
.selected { .preview .selected {
position: absolute;
pointer-events: none; pointer-events: none;
top: 0;
left: 0;
height: 100%;
width: 100%;
} }
.selected .cell { .selected .cell {
background-color: var(--primary-color); background-color: var(--primary-color);

View File

@ -8,6 +8,7 @@ import type { LovelaceCardEditor, LovelaceConfigForm } from "../../types";
import { HuiElementEditor } from "../hui-element-editor"; import { HuiElementEditor } from "../hui-element-editor";
import "./hui-card-layout-editor"; import "./hui-card-layout-editor";
import "./hui-card-visibility-editor"; import "./hui-card-visibility-editor";
import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
const tabs = ["config", "visibility", "layout"] as const; const tabs = ["config", "visibility", "layout"] as const;
@ -16,8 +17,7 @@ export class HuiCardElementEditor extends HuiElementEditor<LovelaceCardConfig> {
@property({ type: Boolean, attribute: "show-visibility-tab" }) @property({ type: Boolean, attribute: "show-visibility-tab" })
public showVisibilityTab = false; public showVisibilityTab = false;
@property({ type: Boolean, attribute: "show-layout-tab" }) @property({ attribute: false }) public sectionConfig?: LovelaceSectionConfig;
public showLayoutTab = false;
@state() private _currTab: (typeof tabs)[number] = tabs[0]; @state() private _currTab: (typeof tabs)[number] = tabs[0];
@ -48,10 +48,18 @@ export class HuiCardElementEditor extends HuiElementEditor<LovelaceCardConfig> {
this.value = ev.detail.value; this.value = ev.detail.value;
} }
get _showLayoutTab(): boolean {
return (
!!this.sectionConfig &&
(this.sectionConfig.type === undefined ||
this.sectionConfig.type === "grid")
);
}
protected renderConfigElement(): TemplateResult { protected renderConfigElement(): TemplateResult {
const displayedTabs: string[] = ["config"]; const displayedTabs: string[] = ["config"];
if (this.showVisibilityTab) displayedTabs.push("visibility"); if (this.showVisibilityTab) displayedTabs.push("visibility");
if (this.showLayoutTab) displayedTabs.push("layout"); if (this._showLayoutTab) displayedTabs.push("layout");
if (displayedTabs.length === 1) return super.renderConfigElement(); if (displayedTabs.length === 1) return super.renderConfigElement();
@ -75,6 +83,7 @@ export class HuiCardElementEditor extends HuiElementEditor<LovelaceCardConfig> {
<hui-card-layout-editor <hui-card-layout-editor
.hass=${this.hass} .hass=${this.hass}
.config=${this.value} .config=${this.value}
.sectionConfig=${this.sectionConfig!}
@value-changed=${this._configChanged} @value-changed=${this._configChanged}
> >
</hui-card-layout-editor> </hui-card-layout-editor>

View File

@ -2,13 +2,13 @@ import type { ActionDetail } from "@material/mwc-list";
import { mdiCheck, mdiDotsVertical } from "@mdi/js"; import { mdiCheck, mdiDotsVertical } from "@mdi/js";
import { css, html, LitElement, nothing, PropertyValues } from "lit"; import { css, html, LitElement, nothing, PropertyValues } from "lit";
import { customElement, property, query, state } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { preventDefault } from "../../../../common/dom/prevent_default"; import { preventDefault } from "../../../../common/dom/prevent_default";
import { stopPropagation } from "../../../../common/dom/stop_propagation"; import { stopPropagation } from "../../../../common/dom/stop_propagation";
import "../../../../components/ha-button"; import "../../../../components/ha-button";
import "../../../../components/ha-button-menu"; import "../../../../components/ha-button-menu";
import "../../../../components/ha-formfield";
import "../../../../components/ha-grid-size-picker"; import "../../../../components/ha-grid-size-picker";
import "../../../../components/ha-icon-button"; import "../../../../components/ha-icon-button";
import "../../../../components/ha-list-item"; import "../../../../components/ha-list-item";
@ -19,6 +19,7 @@ import "../../../../components/ha-switch";
import "../../../../components/ha-yaml-editor"; import "../../../../components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor"; import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import { haStyle } from "../../../../resources/styles"; import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { HuiCard } from "../../cards/hui-card"; import { HuiCard } from "../../cards/hui-card";
@ -34,6 +35,8 @@ export class HuiCardLayoutEditor extends LitElement {
@property({ attribute: false }) public config!: LovelaceCardConfig; @property({ attribute: false }) public config!: LovelaceCardConfig;
@property({ attribute: false }) public sectionConfig!: LovelaceSectionConfig;
@state() _defaultLayoutOptions?: LovelaceLayoutOptions; @state() _defaultLayoutOptions?: LovelaceLayoutOptions;
@state() public _yamlMode = false; @state() public _yamlMode = false;
@ -69,6 +72,8 @@ export class HuiCardLayoutEditor extends LitElement {
const value = this._computeCardGridSize(options); const value = this._computeCardGridSize(options);
const totalColumns = (this.sectionConfig.column_span ?? 1) * 4;
return html` return html`
<div class="header"> <div class="header">
<p class="intro"> <p class="intro">
@ -131,6 +136,10 @@ export class HuiCardLayoutEditor extends LitElement {
` `
: html` : html`
<ha-grid-size-picker <ha-grid-size-picker
style=${styleMap({
"max-width": `${totalColumns * 45 + 50}px`,
})}
.columns=${totalColumns}
.hass=${this.hass} .hass=${this.hass}
.value=${value} .value=${value}
.isDefault=${this._isDefault(this.config.layout_options)} .isDefault=${this._isDefault(this.config.layout_options)}
@ -292,20 +301,12 @@ export class HuiCardLayoutEditor extends LitElement {
} }
ha-grid-size-picker { ha-grid-size-picker {
display: block; display: block;
max-width: 250px;
margin: 16px auto; margin: 16px auto;
} }
ha-yaml-editor { ha-yaml-editor {
display: block; display: block;
margin: 16px 0; margin: 16px 0;
} }
ha-formfield {
display: flex;
align-items: center;
--mdc-typography-body2-font-size: 1em;
max-width: 250px;
margin: 16px auto;
}
`, `,
]; ];
} }

View File

@ -236,8 +236,10 @@ export class HuiDialogEditCard
<div class="content"> <div class="content">
<div class="element-editor"> <div class="element-editor">
<hui-card-element-editor <hui-card-element-editor
.showLayoutTab=${this._shouldShowLayoutTab()}
.showVisibilityTab=${this._cardConfig?.type !== "conditional"} .showVisibilityTab=${this._cardConfig?.type !== "conditional"}
.sectionConfig=${this._isInSection
? this._containerConfig
: undefined}
.hass=${this.hass} .hass=${this.hass}
.lovelace=${this._params.lovelaceConfig} .lovelace=${this._params.lovelaceConfig}
.value=${this._cardConfig} .value=${this._cardConfig}
@ -353,18 +355,6 @@ export class HuiDialogEditCard
return this._params!.path.length === 2; return this._params!.path.length === 2;
} }
private _shouldShowLayoutTab(): boolean {
/**
* Only show layout tab for cards in a grid section
* In the future, every section and view should be able to bring their own editor for layout.
* For now, we limit it to grid sections as it's the only section type
* */
return (
this._isInSection &&
(!this._containerConfig.type || this._containerConfig.type === "grid")
);
}
private _cardConfigInSection = memoizeOne( private _cardConfigInSection = memoizeOne(
(cardConfig?: LovelaceCardConfig) => { (cardConfig?: LovelaceCardConfig) => {
const { cards, title, ...containerConfig } = this const { cards, title, ...containerConfig } = this

View File

@ -35,6 +35,7 @@ import "./hui-section-visibility-editor";
import type { EditSectionDialogParams } from "./show-edit-section-dialog"; import type { EditSectionDialogParams } from "./show-edit-section-dialog";
import "@material/mwc-tab-bar/mwc-tab-bar"; import "@material/mwc-tab-bar/mwc-tab-bar";
import "@material/mwc-tab/mwc-tab"; import "@material/mwc-tab/mwc-tab";
import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
const TABS = ["tab-settings", "tab-visibility"] as const; const TABS = ["tab-settings", "tab-visibility"] as const;
@ -49,6 +50,8 @@ export class HuiDialogEditSection
@state() private _config?: LovelaceSectionRawConfig; @state() private _config?: LovelaceSectionRawConfig;
@state() private _viewConfig?: LovelaceViewConfig;
@state() private _yamlMode = false; @state() private _yamlMode = false;
@state() private _currTab: (typeof TABS)[number] = TABS[0]; @state() private _currTab: (typeof TABS)[number] = TABS[0];
@ -57,10 +60,10 @@ export class HuiDialogEditSection
protected updated(changedProperties: PropertyValues) { protected updated(changedProperties: PropertyValues) {
if (this._yamlMode && changedProperties.has("_yamlMode")) { if (this._yamlMode && changedProperties.has("_yamlMode")) {
const viewConfig = { const sectionConfig = {
...this._config, ...this._config,
}; };
this._editor?.setValue(viewConfig); this._editor?.setValue(sectionConfig);
} }
} }
@ -71,6 +74,9 @@ export class HuiDialogEditSection
this._params.viewIndex, this._params.viewIndex,
this._params.sectionIndex, this._params.sectionIndex,
]); ]);
this._viewConfig = findLovelaceContainer(this._params.lovelaceConfig, [
this._params.viewIndex,
]);
} }
public closeDialog() { public closeDialog() {
@ -107,6 +113,7 @@ export class HuiDialogEditSection
<hui-section-settings-editor <hui-section-settings-editor
.hass=${this.hass} .hass=${this.hass}
.config=${this._config} .config=${this._config}
.viewConfig=${this._viewConfig}
@value-changed=${this._configChanged} @value-changed=${this._configChanged}
> >
</hui-section-settings-editor> </hui-section-settings-editor>

View File

@ -1,23 +1,15 @@
import { LitElement, html } from "lit"; import { LitElement, html } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { LovelaceSectionRawConfig } from "../../../../data/lovelace/config/section"; import memoizeOne from "memoize-one";
import { HomeAssistant } from "../../../../types"; import { fireEvent } from "../../../../common/dom/fire_event";
import { LocalizeFunc } from "../../../../common/translations/localize";
import { import {
HaFormSchema, HaFormSchema,
SchemaUnion, SchemaUnion,
} from "../../../../components/ha-form/types"; } from "../../../../components/ha-form/types";
import { fireEvent } from "../../../../common/dom/fire_event"; import { LovelaceSectionRawConfig } from "../../../../data/lovelace/config/section";
import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
const SCHEMA = [ import { HomeAssistant } from "../../../../types";
{
name: "title",
selector: { text: {} },
},
{
name: "column_span",
selector: { number: { min: 1, max: 3 } },
},
] as const satisfies HaFormSchema[];
type SettingsData = { type SettingsData = {
title: string; title: string;
@ -30,17 +22,46 @@ export class HuiDialogEditSection extends LitElement {
@property({ attribute: false }) public config!: LovelaceSectionRawConfig; @property({ attribute: false }) public config!: LovelaceSectionRawConfig;
@property({ attribute: false }) public viewConfig!: LovelaceViewConfig;
private _schema = memoizeOne(
(localize: LocalizeFunc, maxColumns: number) =>
[
{
name: "title",
selector: { text: {} },
},
{
name: "column_span",
selector: {
number: {
min: 1,
max: maxColumns,
unit_of_measurement: localize(
`ui.panel.lovelace.editor.edit_section.settings.column_span_unit`
),
},
},
},
] as const satisfies HaFormSchema[]
);
render() { render() {
const data: SettingsData = { const data: SettingsData = {
title: this.config.title || "", title: this.config.title || "",
column_span: this.config.column_span || 1, column_span: this.config.column_span || 1,
}; };
const schema = this._schema(
this.hass.localize,
this.viewConfig.max_columns || 4
);
return html` return html`
<ha-form <ha-form
.hass=${this.hass} .hass=${this.hass}
.data=${data} .data=${data}
.schema=${SCHEMA} .schema=${schema}
.computeLabel=${this._computeLabel} .computeLabel=${this._computeLabel}
.computeHelper=${this._computeHelper} .computeHelper=${this._computeHelper}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@ -48,12 +69,16 @@ export class HuiDialogEditSection extends LitElement {
`; `;
} }
private _computeLabel = (schema: SchemaUnion<typeof SCHEMA>) => private _computeLabel = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) =>
this.hass.localize( this.hass.localize(
`ui.panel.lovelace.editor.edit_section.settings.${schema.name}` `ui.panel.lovelace.editor.edit_section.settings.${schema.name}`
); );
private _computeHelper = (schema: SchemaUnion<typeof SCHEMA>) => private _computeHelper = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) =>
this.hass.localize( this.hass.localize(
`ui.panel.lovelace.editor.edit_section.settings.${schema.name}_helper` `ui.panel.lovelace.editor.edit_section.settings.${schema.name}_helper`
) || ""; ) || "";

View File

@ -5661,7 +5661,10 @@
"edit_yaml": "[%key:ui::panel::lovelace::editor::edit_view::edit_yaml%]", "edit_yaml": "[%key:ui::panel::lovelace::editor::edit_view::edit_yaml%]",
"settings": { "settings": {
"title": "Title", "title": "Title",
"title_helper": "The title will appear at the top of section. Leave empty to hide the title." "title_helper": "The title will appear at the top of section. Leave empty to hide the title.",
"column_span": "Size",
"column_span_unit": "columns",
"column_span_helper": "The size may be smaller if less columns are displayed (e.g. on mobile devices)."
}, },
"visibility": { "visibility": {
"explanation": "The section will be shown when ALL conditions below are fulfilled. If no conditions are set, the section will always be shown." "explanation": "The section will be shown when ALL conditions below are fulfilled. If no conditions are set, the section will always be shown."