Migrate title section to heading (#22017)

* Remove title from UI

* Migrate section title to heading card

* Remove title from edit section dialog

* Update src/panels/lovelace/views/hui-sections-view.ts

* Simplify delete section dialog
This commit is contained in:
Paul Bottein 2024-09-23 09:54:20 +02:00 committed by GitHub
parent 0840d8a10e
commit d25f72524b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 94 additions and 86 deletions

View File

@ -36,6 +36,7 @@ import { HassElement } from "../../../../src/state/hass-element";
import { castContext } from "../cast_context";
import "./hc-launch-screen";
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
import { checkLovelaceConfig } from "../../../../src/panels/lovelace/common/check-lovelace-config";
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
strategy: {
@ -365,7 +366,9 @@ export class HcMain extends HassElement {
this._urlPath || "lovelace"
);
castContext.setApplicationState(title || "");
this._lovelaceConfig = lovelaceConfig;
this._lovelaceConfig = checkLovelaceConfig(
lovelaceConfig
) as LovelaceConfig;
}
private _handleShowDemo(_msg: ShowDemoMessage) {

View File

@ -3,10 +3,13 @@ import type { LovelaceCardConfig } from "./card";
import type { LovelaceStrategyConfig } from "./strategy";
export interface LovelaceBaseSectionConfig {
title?: string;
visibility?: Condition[];
column_span?: number;
row_span?: number;
/**
* @deprecated Use heading card instead.
*/
title?: string;
}
export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig {

View File

@ -0,0 +1,68 @@
import {
isStrategySection,
LovelaceSectionRawConfig,
} from "../../../data/lovelace/config/section";
import {
isStrategyDashboard,
LovelaceRawConfig,
} from "../../../data/lovelace/config/types";
import {
isStrategyView,
LovelaceViewRawConfig,
} from "../../../data/lovelace/config/view";
export const checkLovelaceConfig = (
config: LovelaceRawConfig
): LovelaceRawConfig => {
if (isStrategyDashboard(config)) {
return config;
}
const updatedConfig = { ...config };
if (updatedConfig.views) {
updatedConfig.views = updatedConfig.views.map(checkViewConfig);
}
return updatedConfig;
};
export const checkViewConfig = (
view: LovelaceViewRawConfig
): LovelaceViewRawConfig => {
if (isStrategyView(view)) {
return view;
}
const updatedView = { ...view };
// Remove empty badges
if (updatedView.badges && !updatedView.badges.every(Boolean)) {
updatedView.badges = updatedView.badges!.filter(Boolean);
}
// Migrate sections
if (updatedView.sections) {
updatedView.sections = updatedView.sections!.map(checkSectionConfig);
}
return updatedView;
};
export const checkSectionConfig = (
section: LovelaceSectionRawConfig
): LovelaceSectionRawConfig => {
const updatedSection = { ...section };
// Move title to a heading card
if (section.title) {
// Only add card if it's not a strategy section
if (!isStrategySection(updatedSection)) {
const card = { type: "heading", heading: updatedSection.title };
updatedSection.cards = [card, ...(updatedSection.cards || [])];
}
delete updatedSection.title;
}
return updatedSection;
};

View File

@ -11,7 +11,6 @@ import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
import { HomeAssistant } from "../../../../types";
type SettingsData = {
title: string;
column_span?: number;
};
@ -26,10 +25,6 @@ export class HuiDialogEditSection extends LitElement {
private _schema = memoizeOne(
(maxColumns: number) =>
[
{
name: "title",
selector: { text: {} },
},
{
name: "column_span",
selector: {
@ -45,7 +40,6 @@ export class HuiDialogEditSection extends LitElement {
render() {
const data: SettingsData = {
title: this.config.title || "",
column_span: this.config.column_span || 1,
};
@ -83,14 +77,9 @@ export class HuiDialogEditSection extends LitElement {
const newConfig: LovelaceSectionRawConfig = {
...this.config,
title: newData.title,
column_span: newData.column_span,
};
if (!newConfig.title) {
delete newConfig.title;
}
fireEvent(this, "value-changed", { value: newConfig });
}
}

View File

@ -19,10 +19,6 @@ import {
LovelaceRawConfig,
saveConfig,
} from "../../data/lovelace/config/types";
import {
isStrategyView,
LovelaceViewConfig,
} from "../../data/lovelace/config/view";
import { fetchResources } from "../../data/lovelace/resource";
import { WindowWithPreloads } from "../../data/preloads";
import "../../layouts/hass-error-screen";
@ -30,6 +26,7 @@ import "../../layouts/hass-loading-screen";
import { HomeAssistant, PanelInfo, Route } from "../../types";
import { showToast } from "../../util/toast";
import { loadLovelaceResources } from "./common/load-resources";
import { checkLovelaceConfig } from "./common/check-lovelace-config";
import { showSaveDialog } from "./editor/show-save-config-dialog";
import "./hui-root";
import { generateLovelaceDashboardStrategy } from "./strategies/get-strategy";
@ -321,26 +318,8 @@ export class LovelacePanel extends LitElement {
}
private _checkLovelaceConfig(config: LovelaceRawConfig) {
// Somehow there can be badges with value null, we remove those
if (isStrategyDashboard(config)) {
return config;
}
let checkedConfig = !Object.isFrozen(config) ? config : undefined;
config.views.forEach((view, index) => {
if (isStrategyView(view)) {
return;
}
if (view.badges && !view.badges.every(Boolean)) {
checkedConfig = checkedConfig || {
...config,
views: [...config.views],
};
const updatedView = { ...view } as LovelaceViewConfig;
updatedView.badges = view.badges.filter(Boolean);
checkedConfig.views[index] = updatedView;
}
});
return checkedConfig ? deepFreeze(checkedConfig) : config;
const checkedConfig = checkLovelaceConfig(config);
return deepFreeze(checkedConfig);
}
private _setLovelaceConfig(

View File

@ -1,5 +1,5 @@
import { ResizeController } from "@lit-labs/observers/resize-controller";
import { mdiArrowAll, mdiDelete, mdiPencil, mdiViewGridPlus } from "@mdi/js";
import { mdiDelete, mdiDrag, mdiPencil, mdiViewGridPlus } from "@mdi/js";
import {
CSSResultGroup,
LitElement,
@ -182,26 +182,16 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
})}
>
${
sectionConfig?.title || this.lovelace?.editMode
this.lovelace?.editMode
? html`
<div class="section-header">
<h2
class="section-title ${classMap({
placeholder: !sectionConfig?.title,
})}"
>
${sectionConfig?.title ||
this.hass.localize(
"ui.panel.lovelace.editor.section.unnamed_section"
)}
</h2>
${editMode
? html`
<div class="section-actions">
<ha-svg-icon
aria-hidden="true"
class="handle"
.path=${mdiArrowAll}
.path=${mdiDrag}
></ha-svg-icon>
<ha-icon-button
.label=${this.hass.localize(
@ -256,7 +246,12 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
private _createSection(): void {
const newConfig = addSection(this.lovelace!.config, this.index!, {
type: "grid",
cards: [],
cards: [
{
type: "heading",
heading: "New Section",
},
],
});
this.lovelace!.saveConfig(newConfig);
}
@ -281,20 +276,15 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
const section = findLovelaceContainer(this.lovelace!.config, path);
const title = section.title?.trim();
const cardCount = "cards" in section && section.cards?.length;
if (title || cardCount) {
const named = title ? "named" : "unnamed";
const type = cardCount ? "cards" : "only";
if (cardCount) {
const confirm = await showConfirmationDialog(this, {
title: this.hass.localize(
"ui.panel.lovelace.editor.delete_section.title"
),
text: this.hass.localize(
`ui.panel.lovelace.editor.delete_section.text_${named}_section_${type}`,
{ name: title }
`ui.panel.lovelace.editor.delete_section.text`
),
confirmText: this.hass.localize("ui.common.delete"),
destructive: true,
@ -380,7 +370,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
}
.create-section {
margin-top: calc(var(--row-height) + var(--row-gap));
margin-top: 36px;
outline: none;
background: none;
cursor: pointer;
@ -408,35 +398,16 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
.section-header {
position: relative;
height: var(--row-height);
margin-bottom: var(--row-gap);
height: 34px;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.section-title {
color: var(--primary-text-color);
font-size: 20px;
font-weight: normal;
margin: 0px;
letter-spacing: 0.1px;
line-height: 32px;
text-align: var(--ha-view-sections-title-text-align, start);
min-height: 32px;
box-sizing: border-box;
padding: 0 10px 10px;
}
.section-title.placeholder {
color: var(--secondary-text-color);
font-style: italic;
}
.section-actions {
position: absolute;
height: 36px;
bottom: calc(-1 * var(--row-gap) - 2px);
bottom: -2px;
right: 0;
inset-inline-end: 0;
inset-inline-start: initial;
@ -445,7 +416,6 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
align-items: center;
justify-content: center;
transition: opacity 0.2s ease-in-out;
background-color: rgba(var(--rgb-card-background-color), 0.3);
border-radius: var(--ha-card-border-radius, 12px);
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;

View File

@ -5655,17 +5655,13 @@
"strategy_type": "strategy"
},
"section": {
"unnamed_section": "Unnamed section",
"add_badge": "Add badge",
"add_card": "[%key:ui::panel::lovelace::editor::edit_card::add%]",
"create_section": "Create section"
},
"delete_section": {
"title": "Delete section",
"text_named_section_only": "''{name}'' section will be deleted.",
"text_unnamed_section_only": "This section will be deleted.",
"text_named_section_cards": "''{name}'' section and all its cards will be deleted.",
"text_unnamed_section_cards": "This section and all its cards will be deleted."
"text": "This section and all its cards will be deleted."
},
"edit_section": {
"header": "Edit section",