mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 03:36:44 +00:00
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:
parent
0840d8a10e
commit
d25f72524b
@ -36,6 +36,7 @@ import { HassElement } from "../../../../src/state/hass-element";
|
|||||||
import { castContext } from "../cast_context";
|
import { castContext } from "../cast_context";
|
||||||
import "./hc-launch-screen";
|
import "./hc-launch-screen";
|
||||||
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
|
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
|
||||||
|
import { checkLovelaceConfig } from "../../../../src/panels/lovelace/common/check-lovelace-config";
|
||||||
|
|
||||||
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
|
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
|
||||||
strategy: {
|
strategy: {
|
||||||
@ -365,7 +366,9 @@ export class HcMain extends HassElement {
|
|||||||
this._urlPath || "lovelace"
|
this._urlPath || "lovelace"
|
||||||
);
|
);
|
||||||
castContext.setApplicationState(title || "");
|
castContext.setApplicationState(title || "");
|
||||||
this._lovelaceConfig = lovelaceConfig;
|
this._lovelaceConfig = checkLovelaceConfig(
|
||||||
|
lovelaceConfig
|
||||||
|
) as LovelaceConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleShowDemo(_msg: ShowDemoMessage) {
|
private _handleShowDemo(_msg: ShowDemoMessage) {
|
||||||
|
@ -3,10 +3,13 @@ import type { LovelaceCardConfig } from "./card";
|
|||||||
import type { LovelaceStrategyConfig } from "./strategy";
|
import type { LovelaceStrategyConfig } from "./strategy";
|
||||||
|
|
||||||
export interface LovelaceBaseSectionConfig {
|
export interface LovelaceBaseSectionConfig {
|
||||||
title?: string;
|
|
||||||
visibility?: Condition[];
|
visibility?: Condition[];
|
||||||
column_span?: number;
|
column_span?: number;
|
||||||
row_span?: number;
|
row_span?: number;
|
||||||
|
/**
|
||||||
|
* @deprecated Use heading card instead.
|
||||||
|
*/
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig {
|
export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig {
|
||||||
|
68
src/panels/lovelace/common/check-lovelace-config.ts
Normal file
68
src/panels/lovelace/common/check-lovelace-config.ts
Normal 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;
|
||||||
|
};
|
@ -11,7 +11,6 @@ import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
type SettingsData = {
|
type SettingsData = {
|
||||||
title: string;
|
|
||||||
column_span?: number;
|
column_span?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,10 +25,6 @@ export class HuiDialogEditSection extends LitElement {
|
|||||||
private _schema = memoizeOne(
|
private _schema = memoizeOne(
|
||||||
(maxColumns: number) =>
|
(maxColumns: number) =>
|
||||||
[
|
[
|
||||||
{
|
|
||||||
name: "title",
|
|
||||||
selector: { text: {} },
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "column_span",
|
name: "column_span",
|
||||||
selector: {
|
selector: {
|
||||||
@ -45,7 +40,6 @@ export class HuiDialogEditSection extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const data: SettingsData = {
|
const data: SettingsData = {
|
||||||
title: this.config.title || "",
|
|
||||||
column_span: this.config.column_span || 1,
|
column_span: this.config.column_span || 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,14 +77,9 @@ export class HuiDialogEditSection extends LitElement {
|
|||||||
|
|
||||||
const newConfig: LovelaceSectionRawConfig = {
|
const newConfig: LovelaceSectionRawConfig = {
|
||||||
...this.config,
|
...this.config,
|
||||||
title: newData.title,
|
|
||||||
column_span: newData.column_span,
|
column_span: newData.column_span,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!newConfig.title) {
|
|
||||||
delete newConfig.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
fireEvent(this, "value-changed", { value: newConfig });
|
fireEvent(this, "value-changed", { value: newConfig });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,6 @@ import {
|
|||||||
LovelaceRawConfig,
|
LovelaceRawConfig,
|
||||||
saveConfig,
|
saveConfig,
|
||||||
} from "../../data/lovelace/config/types";
|
} from "../../data/lovelace/config/types";
|
||||||
import {
|
|
||||||
isStrategyView,
|
|
||||||
LovelaceViewConfig,
|
|
||||||
} from "../../data/lovelace/config/view";
|
|
||||||
import { fetchResources } from "../../data/lovelace/resource";
|
import { fetchResources } from "../../data/lovelace/resource";
|
||||||
import { WindowWithPreloads } from "../../data/preloads";
|
import { WindowWithPreloads } from "../../data/preloads";
|
||||||
import "../../layouts/hass-error-screen";
|
import "../../layouts/hass-error-screen";
|
||||||
@ -30,6 +26,7 @@ import "../../layouts/hass-loading-screen";
|
|||||||
import { HomeAssistant, PanelInfo, Route } from "../../types";
|
import { HomeAssistant, PanelInfo, Route } from "../../types";
|
||||||
import { showToast } from "../../util/toast";
|
import { showToast } from "../../util/toast";
|
||||||
import { loadLovelaceResources } from "./common/load-resources";
|
import { loadLovelaceResources } from "./common/load-resources";
|
||||||
|
import { checkLovelaceConfig } from "./common/check-lovelace-config";
|
||||||
import { showSaveDialog } from "./editor/show-save-config-dialog";
|
import { showSaveDialog } from "./editor/show-save-config-dialog";
|
||||||
import "./hui-root";
|
import "./hui-root";
|
||||||
import { generateLovelaceDashboardStrategy } from "./strategies/get-strategy";
|
import { generateLovelaceDashboardStrategy } from "./strategies/get-strategy";
|
||||||
@ -321,26 +318,8 @@ export class LovelacePanel extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _checkLovelaceConfig(config: LovelaceRawConfig) {
|
private _checkLovelaceConfig(config: LovelaceRawConfig) {
|
||||||
// Somehow there can be badges with value null, we remove those
|
const checkedConfig = checkLovelaceConfig(config);
|
||||||
if (isStrategyDashboard(config)) {
|
return deepFreeze(checkedConfig);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setLovelaceConfig(
|
private _setLovelaceConfig(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ResizeController } from "@lit-labs/observers/resize-controller";
|
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 {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
LitElement,
|
LitElement,
|
||||||
@ -182,26 +182,16 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
${
|
${
|
||||||
sectionConfig?.title || this.lovelace?.editMode
|
this.lovelace?.editMode
|
||||||
? html`
|
? html`
|
||||||
<div class="section-header">
|
<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
|
${editMode
|
||||||
? html`
|
? html`
|
||||||
<div class="section-actions">
|
<div class="section-actions">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="handle"
|
class="handle"
|
||||||
.path=${mdiArrowAll}
|
.path=${mdiDrag}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -256,7 +246,12 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
|||||||
private _createSection(): void {
|
private _createSection(): void {
|
||||||
const newConfig = addSection(this.lovelace!.config, this.index!, {
|
const newConfig = addSection(this.lovelace!.config, this.index!, {
|
||||||
type: "grid",
|
type: "grid",
|
||||||
cards: [],
|
cards: [
|
||||||
|
{
|
||||||
|
type: "heading",
|
||||||
|
heading: "New Section",
|
||||||
|
},
|
||||||
|
],
|
||||||
});
|
});
|
||||||
this.lovelace!.saveConfig(newConfig);
|
this.lovelace!.saveConfig(newConfig);
|
||||||
}
|
}
|
||||||
@ -281,20 +276,15 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
|||||||
|
|
||||||
const section = findLovelaceContainer(this.lovelace!.config, path);
|
const section = findLovelaceContainer(this.lovelace!.config, path);
|
||||||
|
|
||||||
const title = section.title?.trim();
|
|
||||||
const cardCount = "cards" in section && section.cards?.length;
|
const cardCount = "cards" in section && section.cards?.length;
|
||||||
|
|
||||||
if (title || cardCount) {
|
if (cardCount) {
|
||||||
const named = title ? "named" : "unnamed";
|
|
||||||
const type = cardCount ? "cards" : "only";
|
|
||||||
|
|
||||||
const confirm = await showConfirmationDialog(this, {
|
const confirm = await showConfirmationDialog(this, {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.lovelace.editor.delete_section.title"
|
"ui.panel.lovelace.editor.delete_section.title"
|
||||||
),
|
),
|
||||||
text: this.hass.localize(
|
text: this.hass.localize(
|
||||||
`ui.panel.lovelace.editor.delete_section.text_${named}_section_${type}`,
|
`ui.panel.lovelace.editor.delete_section.text`
|
||||||
{ name: title }
|
|
||||||
),
|
),
|
||||||
confirmText: this.hass.localize("ui.common.delete"),
|
confirmText: this.hass.localize("ui.common.delete"),
|
||||||
destructive: true,
|
destructive: true,
|
||||||
@ -380,7 +370,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.create-section {
|
.create-section {
|
||||||
margin-top: calc(var(--row-height) + var(--row-gap));
|
margin-top: 36px;
|
||||||
outline: none;
|
outline: none;
|
||||||
background: none;
|
background: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -408,35 +398,16 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
|||||||
|
|
||||||
.section-header {
|
.section-header {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: var(--row-height);
|
height: 34px;
|
||||||
margin-bottom: var(--row-gap);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: flex-end;
|
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 {
|
.section-actions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
bottom: calc(-1 * var(--row-gap) - 2px);
|
bottom: -2px;
|
||||||
right: 0;
|
right: 0;
|
||||||
inset-inline-end: 0;
|
inset-inline-end: 0;
|
||||||
inset-inline-start: initial;
|
inset-inline-start: initial;
|
||||||
@ -445,7 +416,6 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: opacity 0.2s ease-in-out;
|
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-radius: var(--ha-card-border-radius, 12px);
|
||||||
border-bottom-left-radius: 0px;
|
border-bottom-left-radius: 0px;
|
||||||
border-bottom-right-radius: 0px;
|
border-bottom-right-radius: 0px;
|
||||||
|
@ -5655,17 +5655,13 @@
|
|||||||
"strategy_type": "strategy"
|
"strategy_type": "strategy"
|
||||||
},
|
},
|
||||||
"section": {
|
"section": {
|
||||||
"unnamed_section": "Unnamed section",
|
|
||||||
"add_badge": "Add badge",
|
"add_badge": "Add badge",
|
||||||
"add_card": "[%key:ui::panel::lovelace::editor::edit_card::add%]",
|
"add_card": "[%key:ui::panel::lovelace::editor::edit_card::add%]",
|
||||||
"create_section": "Create section"
|
"create_section": "Create section"
|
||||||
},
|
},
|
||||||
"delete_section": {
|
"delete_section": {
|
||||||
"title": "Delete section",
|
"title": "Delete section",
|
||||||
"text_named_section_only": "''{name}'' section will be deleted.",
|
"text": "This section and all its cards 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."
|
|
||||||
},
|
},
|
||||||
"edit_section": {
|
"edit_section": {
|
||||||
"header": "Edit section",
|
"header": "Edit section",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user