mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-21 18:13:04 +00:00
Compare commits
4 Commits
card_picke
...
badges-int
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f1e4ea942 | ||
|
|
d46c686118 | ||
|
|
3aea6a13ec | ||
|
|
f1c26dc747 |
@@ -93,6 +93,7 @@ export class HaSelectSelector extends LitElement {
|
||||
<ha-select-box
|
||||
.options=${options}
|
||||
.value=${this.value as string | undefined}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._selectChanged}
|
||||
.maxColumns=${this.selector.select?.box_max_columns}
|
||||
.hass=${this.hass}
|
||||
|
||||
@@ -32,7 +32,7 @@ export interface LovelaceViewBackgroundConfig {
|
||||
|
||||
export interface LovelaceViewHeaderConfig {
|
||||
card?: LovelaceCardConfig;
|
||||
layout?: "start" | "center" | "responsive";
|
||||
layout?: "start" | "center" | "responsive" | "integrated";
|
||||
badges_position?: "bottom" | "top";
|
||||
badges_wrap?: "wrap" | "scroll";
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { PropertyValues } from "lit";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -9,15 +10,13 @@ import type {
|
||||
HaFormSchema,
|
||||
SchemaUnion,
|
||||
} from "../../../../components/ha-form/types";
|
||||
import type {
|
||||
LovelaceViewConfig,
|
||||
LovelaceViewHeaderConfig,
|
||||
} from "../../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
import type { LovelaceViewHeaderConfig } from "../../../../data/lovelace/config/view";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../../../../types";
|
||||
import {
|
||||
DEFAULT_VIEW_HEADER_BADGES_POSITION,
|
||||
DEFAULT_VIEW_HEADER_BADGES_WRAP,
|
||||
DEFAULT_VIEW_HEADER_LAYOUT,
|
||||
VIEW_HEADER_LAYOUT_INTEGRATED,
|
||||
} from "../../views/hui-view-header";
|
||||
import { listenMediaQuery } from "../../../../common/dom/media_query";
|
||||
|
||||
@@ -29,6 +28,8 @@ export class HuiViewHeaderSettingsEditor extends LitElement {
|
||||
|
||||
@state({ attribute: false }) private narrow = false;
|
||||
|
||||
@state() private _selectedLayout = DEFAULT_VIEW_HEADER_LAYOUT;
|
||||
|
||||
private _unsubMql?: () => void;
|
||||
|
||||
connectedCallback(): void {
|
||||
@@ -44,16 +45,32 @@ export class HuiViewHeaderSettingsEditor extends LitElement {
|
||||
this._unsubMql = undefined;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProperties: PropertyValues<this>): void {
|
||||
if (changedProperties.has("config")) {
|
||||
this._selectedLayout = this.config?.layout ?? DEFAULT_VIEW_HEADER_LAYOUT;
|
||||
}
|
||||
}
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc, isRTL: boolean, narrow: boolean) =>
|
||||
(
|
||||
localize: LocalizeFunc,
|
||||
isRTL: boolean,
|
||||
narrow: boolean,
|
||||
integratedLayout: boolean
|
||||
) =>
|
||||
[
|
||||
{
|
||||
name: "layout",
|
||||
selector: {
|
||||
select: {
|
||||
mode: "box",
|
||||
box_max_columns: narrow ? 1 : 3,
|
||||
options: ["responsive", "start", "center"].map((value) => {
|
||||
box_max_columns: narrow ? 1 : 4,
|
||||
options: [
|
||||
"responsive",
|
||||
"start",
|
||||
"center",
|
||||
VIEW_HEADER_LAYOUT_INTEGRATED,
|
||||
].map((value) => {
|
||||
const labelKey =
|
||||
value === "start" && isRTL ? `${value}_rtl` : value;
|
||||
return {
|
||||
@@ -65,8 +82,8 @@ export class HuiViewHeaderSettingsEditor extends LitElement {
|
||||
`ui.panel.lovelace.editor.edit_view_header.settings.layout_options.${value}_description`
|
||||
),
|
||||
image: {
|
||||
src: `/static/images/form/view_header_layout_${value}.svg`,
|
||||
src_dark: `/static/images/form/view_header_layout_${value}_dark.svg`,
|
||||
src: `/static/images/form/view_header_layout_${value === VIEW_HEADER_LAYOUT_INTEGRATED ? "responsive" : value}.svg`,
|
||||
src_dark: `/static/images/form/view_header_layout_${value === VIEW_HEADER_LAYOUT_INTEGRATED ? "responsive" : value}_dark.svg`,
|
||||
flip_rtl: true,
|
||||
},
|
||||
};
|
||||
@@ -76,6 +93,7 @@ export class HuiViewHeaderSettingsEditor extends LitElement {
|
||||
},
|
||||
{
|
||||
name: "badges_position",
|
||||
disabled: integratedLayout,
|
||||
selector: {
|
||||
select: {
|
||||
mode: "box",
|
||||
@@ -95,6 +113,7 @@ export class HuiViewHeaderSettingsEditor extends LitElement {
|
||||
},
|
||||
{
|
||||
name: "badges_wrap",
|
||||
disabled: integratedLayout,
|
||||
selector: {
|
||||
select: {
|
||||
mode: "box",
|
||||
@@ -125,16 +144,27 @@ export class HuiViewHeaderSettingsEditor extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const layout = this._selectedLayout;
|
||||
const integratedLayout = layout === VIEW_HEADER_LAYOUT_INTEGRATED;
|
||||
|
||||
const data = {
|
||||
layout: this.config?.layout || DEFAULT_VIEW_HEADER_LAYOUT,
|
||||
badges_position:
|
||||
this.config?.badges_position || DEFAULT_VIEW_HEADER_BADGES_POSITION,
|
||||
badges_wrap: this.config?.badges_wrap || DEFAULT_VIEW_HEADER_BADGES_WRAP,
|
||||
layout,
|
||||
badges_position: integratedLayout
|
||||
? "top"
|
||||
: (this.config?.badges_position ?? DEFAULT_VIEW_HEADER_BADGES_POSITION),
|
||||
badges_wrap: integratedLayout
|
||||
? "scroll"
|
||||
: (this.config?.badges_wrap ?? DEFAULT_VIEW_HEADER_BADGES_WRAP),
|
||||
};
|
||||
|
||||
const narrow = this.narrow;
|
||||
const isRTL = computeRTL(this.hass);
|
||||
const schema = this._schema(this.hass.localize, isRTL, narrow);
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
isRTL,
|
||||
narrow,
|
||||
integratedLayout
|
||||
);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
@@ -147,15 +177,27 @@ export class HuiViewHeaderSettingsEditor extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
private _valueChanged(ev: ValueChangedEvent<LovelaceViewHeaderConfig>): void {
|
||||
ev.stopPropagation();
|
||||
const newData = ev.detail.value as LovelaceViewConfig;
|
||||
|
||||
const layout =
|
||||
ev.detail.value.layout ??
|
||||
this._selectedLayout ??
|
||||
DEFAULT_VIEW_HEADER_LAYOUT;
|
||||
this._selectedLayout = layout;
|
||||
|
||||
const integratedLayout = layout === VIEW_HEADER_LAYOUT_INTEGRATED;
|
||||
|
||||
const config: LovelaceViewHeaderConfig = {
|
||||
...this.config,
|
||||
...newData,
|
||||
...ev.detail.value,
|
||||
};
|
||||
|
||||
if (integratedLayout) {
|
||||
config.badges_position = "top";
|
||||
config.badges_wrap = "scroll";
|
||||
}
|
||||
|
||||
fireEvent(this, "config-changed", { config });
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import { ifDefined } from "lit/directives/if-defined";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { UndoRedoController } from "../../common/controllers/undo-redo-controller";
|
||||
import { DragScrollController } from "../../common/controllers/drag-scroll-controller";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { goBack, navigate } from "../../common/navigate";
|
||||
import type { LocalizeKeys } from "../../common/translations/localize";
|
||||
@@ -50,6 +51,7 @@ import "../../components/ha-tab-group";
|
||||
import "../../components/ha-tab-group-tab";
|
||||
import "../../components/ha-tooltip";
|
||||
import { createAreaRegistryEntry } from "../../data/area/area_registry";
|
||||
import type { LovelaceViewElement } from "../../data/lovelace";
|
||||
import type {
|
||||
LovelaceConfig,
|
||||
LovelaceRawConfig,
|
||||
@@ -61,6 +63,7 @@ import {
|
||||
fetchDashboards,
|
||||
updateDashboard,
|
||||
} from "../../data/lovelace/dashboard";
|
||||
import { isStrategyView } from "../../data/lovelace/config/view";
|
||||
import { fetchLovelaceInfo } from "../../data/lovelace/resource";
|
||||
import { getPanelTitle } from "../../data/panel";
|
||||
import { createPerson } from "../../data/person";
|
||||
@@ -90,12 +93,16 @@ import { showSaveDialog } from "./editor/show-save-config-dialog";
|
||||
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
|
||||
import { getLovelaceStrategy } from "./strategies/get-strategy";
|
||||
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
|
||||
import type { HuiBadge } from "./badges/hui-badge";
|
||||
import type { Lovelace } from "./types";
|
||||
import "./badges/hui-view-badges";
|
||||
import "./views/hui-view";
|
||||
import type { HUIView } from "./views/hui-view";
|
||||
import "./views/hui-view-background";
|
||||
import "./views/hui-view-container";
|
||||
|
||||
const VIEW_HEADER_LAYOUT_INTEGRATED = "integrated";
|
||||
|
||||
interface ActionItem {
|
||||
icon: string;
|
||||
key: LocalizeKeys;
|
||||
@@ -158,6 +165,11 @@ class HUIRoot extends LitElement {
|
||||
|
||||
private _viewCache: Record<string, HUIView> = {};
|
||||
|
||||
private _toolbarBadgeDragScrollController = new DragScrollController(this, {
|
||||
selector: ".toolbar-badges.scroll",
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
private _viewScrollPositions: Record<string, number> = {};
|
||||
|
||||
private _restoreScroll = false;
|
||||
@@ -538,6 +550,16 @@ class HUIRoot extends LitElement {
|
||||
|
||||
const isSubview = curViewConfig?.subview;
|
||||
const hasTabViews = views.filter((view) => !view.subview).length > 1;
|
||||
const headerConfig =
|
||||
curViewConfig && !isStrategyView(curViewConfig)
|
||||
? curViewConfig.header
|
||||
: undefined;
|
||||
const showToolbarBadges =
|
||||
!this._editMode &&
|
||||
!this.narrow &&
|
||||
headerConfig?.layout === VIEW_HEADER_LAYOUT_INTEGRATED &&
|
||||
typeof this._curView === "number";
|
||||
const toolbarBadges = showToolbarBadges ? this._getCurrentViewBadges() : [];
|
||||
|
||||
return html`
|
||||
<div
|
||||
@@ -548,7 +570,12 @@ class HUIRoot extends LitElement {
|
||||
>
|
||||
<div class="header">
|
||||
<slot name="toolbar">
|
||||
<div class="toolbar">
|
||||
<div
|
||||
class=${classMap({
|
||||
toolbar: true,
|
||||
"integrated-badges": showToolbarBadges,
|
||||
})}
|
||||
>
|
||||
${this._editMode
|
||||
? html`
|
||||
<div class="main-title">
|
||||
@@ -593,6 +620,27 @@ class HUIRoot extends LitElement {
|
||||
${views[0]?.title ?? dashboardTitle}
|
||||
</div>
|
||||
`}
|
||||
${showToolbarBadges && toolbarBadges.length > 0
|
||||
? html`
|
||||
<div
|
||||
class=${classMap({
|
||||
"toolbar-badges": true,
|
||||
scroll: true,
|
||||
dragging:
|
||||
this._toolbarBadgeDragScrollController
|
||||
.scrolling,
|
||||
})}
|
||||
>
|
||||
<hui-view-badges
|
||||
.badges=${toolbarBadges}
|
||||
.hass=${this.hass}
|
||||
.lovelace=${this.lovelace!}
|
||||
.viewIndex=${this._curView as number}
|
||||
.showAddLabel=${false}
|
||||
></hui-view-badges>
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
<div class="action-items">${this._renderActionItems()}</div>
|
||||
`}
|
||||
</div>
|
||||
@@ -826,6 +874,23 @@ class HUIRoot extends LitElement {
|
||||
this._selectView(newSelectView);
|
||||
});
|
||||
}
|
||||
|
||||
const currentViewConfig =
|
||||
typeof this._curView === "number"
|
||||
? this.config.views[this._curView]
|
||||
: undefined;
|
||||
const currentViewHeaderConfig =
|
||||
currentViewConfig && !isStrategyView(currentViewConfig)
|
||||
? currentViewConfig.header
|
||||
: undefined;
|
||||
const integratedLayout =
|
||||
currentViewHeaderConfig?.layout === VIEW_HEADER_LAYOUT_INTEGRATED;
|
||||
const toolbarBadges = this._getCurrentViewBadges();
|
||||
this._toolbarBadgeDragScrollController.enabled =
|
||||
!this._editMode &&
|
||||
!this.narrow &&
|
||||
integratedLayout &&
|
||||
toolbarBadges.length > 0;
|
||||
}
|
||||
|
||||
private get config(): LovelaceConfig {
|
||||
@@ -844,6 +909,16 @@ class HUIRoot extends LitElement {
|
||||
return this.shadowRoot!.getElementById("view") as HTMLDivElement;
|
||||
}
|
||||
|
||||
private _getCurrentViewBadges(): HuiBadge[] {
|
||||
if (typeof this._curView !== "number") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const view = this._viewCache[this._curView];
|
||||
const layout = view?.firstElementChild as LovelaceViewElement | null;
|
||||
return layout?.badges || [];
|
||||
}
|
||||
|
||||
private _handleRefresh = () => {
|
||||
fireEvent(this, "config-refresh");
|
||||
};
|
||||
@@ -1323,7 +1398,7 @@ class HUIRoot extends LitElement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
padding: 0px 12px;
|
||||
padding: 0 var(--ha-space-3);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -1331,7 +1406,7 @@ class HUIRoot extends LitElement {
|
||||
border-bottom: none;
|
||||
}
|
||||
.narrow .toolbar {
|
||||
padding: 0 4px;
|
||||
padding: 0 var(--ha-space-1);
|
||||
}
|
||||
.main-title {
|
||||
margin-inline-start: var(--ha-space-6);
|
||||
@@ -1342,6 +1417,9 @@ class HUIRoot extends LitElement {
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
}
|
||||
.toolbar.integrated-badges .main-title {
|
||||
flex-grow: 0;
|
||||
}
|
||||
.narrow .main-title {
|
||||
margin-inline-start: var(--ha-space-2);
|
||||
}
|
||||
@@ -1350,6 +1428,37 @@ class HUIRoot extends LitElement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.toolbar-badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
width: auto;
|
||||
flex: 1 1 0;
|
||||
max-width: none;
|
||||
margin-inline-start: var(--ha-space-4);
|
||||
margin-inline-end: var(--ha-space-1);
|
||||
}
|
||||
.toolbar-badges.scroll {
|
||||
overflow: auto;
|
||||
scrollbar-color: var(--scrollbar-thumb-color) transparent;
|
||||
scrollbar-width: none;
|
||||
mask-image: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
black 16px,
|
||||
black calc(100% - 16px),
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
.toolbar-badges.dragging {
|
||||
pointer-events: none;
|
||||
}
|
||||
.toolbar-badges hui-view-badges {
|
||||
width: 100%;
|
||||
--badges-wrap: nowrap;
|
||||
--badges-aligmnent: flex-start;
|
||||
--badge-padding: var(--ha-space-4);
|
||||
}
|
||||
ha-tab-group {
|
||||
--ha-tab-indicator-color: var(
|
||||
--app-header-selection-bar-color,
|
||||
|
||||
@@ -202,6 +202,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
|
||||
<hui-view-header
|
||||
.hass=${this.hass}
|
||||
.badges=${this.badges}
|
||||
.narrow=${this.narrow}
|
||||
.lovelace=${this.lovelace}
|
||||
.viewIndex=${this.index}
|
||||
.config=${this._config?.header}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { showEditViewHeaderDialog } from "../editor/view-header/show-edit-view-h
|
||||
import type { Lovelace } from "../types";
|
||||
|
||||
export const DEFAULT_VIEW_HEADER_LAYOUT = "center";
|
||||
export const VIEW_HEADER_LAYOUT_INTEGRATED = "integrated";
|
||||
export const DEFAULT_VIEW_HEADER_BADGES_POSITION = "bottom";
|
||||
export const DEFAULT_VIEW_HEADER_BADGES_WRAP = "wrap";
|
||||
|
||||
@@ -32,6 +33,8 @@ export class HuiViewHeader extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public lovelace!: Lovelace;
|
||||
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: false }) public card?: HuiCard;
|
||||
|
||||
@property({ attribute: false }) public badges: HuiBadge[] = [];
|
||||
@@ -202,9 +205,9 @@ export class HuiViewHeader extends LitElement {
|
||||
this.config?.badges_position ?? DEFAULT_VIEW_HEADER_BADGES_POSITION;
|
||||
const badgesWrap =
|
||||
this.config?.badges_wrap ?? DEFAULT_VIEW_HEADER_BADGES_WRAP;
|
||||
const badgeDragging = this._dragScrollController.scrolling
|
||||
? "dragging"
|
||||
: "";
|
||||
const badgeDragging = this._dragScrollController.scrolling;
|
||||
const badgesInToolbar =
|
||||
layout === VIEW_HEADER_LAYOUT_INTEGRATED && !editMode && !this.narrow;
|
||||
|
||||
const hasHeading = card !== undefined;
|
||||
const hasBadges = this.badges.length > 0;
|
||||
@@ -264,10 +267,17 @@ export class HuiViewHeader extends LitElement {
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
${this.lovelace && (editMode || this.badges.length > 0)
|
||||
${this.lovelace &&
|
||||
!badgesInToolbar &&
|
||||
(editMode || this.badges.length > 0)
|
||||
? html`
|
||||
<div
|
||||
class="badges ${badgesPosition} ${badgesWrap} ${badgeDragging}"
|
||||
class=${classMap({
|
||||
badges: true,
|
||||
[badgesPosition]: true,
|
||||
[badgesWrap]: true,
|
||||
dragging: badgeDragging,
|
||||
})}
|
||||
>
|
||||
<hui-view-badges
|
||||
.badges=${this.badges}
|
||||
|
||||
@@ -8695,7 +8695,9 @@
|
||||
"start_description": "Always stacked",
|
||||
"start_rtl": "Right aligned",
|
||||
"center": "Centered",
|
||||
"center_description": "Always stacked"
|
||||
"center_description": "Always stacked",
|
||||
"integrated": "Integrated into toolbar",
|
||||
"integrated_description": "Badges are shown in the top app bar on desktop"
|
||||
},
|
||||
"badges_position": "Badges position",
|
||||
"badges_position_options": {
|
||||
|
||||
Reference in New Issue
Block a user