mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 10:16:46 +00:00
Merge branch 'rc'
This commit is contained in:
commit
f0341f28ab
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20250506.0"
|
version = "20250507.0"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
license-files = ["LICENSE*"]
|
license-files = ["LICENSE*"]
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
|
@ -9,6 +9,7 @@ import type { LitElement } from "lit";
|
|||||||
*/
|
*/
|
||||||
export interface DragScrollControllerConfig {
|
export interface DragScrollControllerConfig {
|
||||||
selector: string;
|
selector: string;
|
||||||
|
enabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DragScrollController implements ReactiveController {
|
export class DragScrollController implements ReactiveController {
|
||||||
@ -28,19 +29,47 @@ export class DragScrollController implements ReactiveController {
|
|||||||
|
|
||||||
private _scrollContainer?: HTMLElement | null;
|
private _scrollContainer?: HTMLElement | null;
|
||||||
|
|
||||||
|
private _enabled = true;
|
||||||
|
|
||||||
|
public get enabled(): boolean {
|
||||||
|
return this._enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set enabled(value: boolean) {
|
||||||
|
if (value === this._enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._enabled = value;
|
||||||
|
if (this._enabled) {
|
||||||
|
this._attach();
|
||||||
|
} else {
|
||||||
|
this._detach();
|
||||||
|
}
|
||||||
|
this._host.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
host: ReactiveControllerHost & LitElement,
|
host: ReactiveControllerHost & LitElement,
|
||||||
{ selector }: DragScrollControllerConfig
|
{ selector, enabled }: DragScrollControllerConfig
|
||||||
) {
|
) {
|
||||||
this._selector = selector;
|
this._selector = selector;
|
||||||
this._host = host;
|
this._host = host;
|
||||||
|
this.enabled = enabled ?? true;
|
||||||
host.addController(this);
|
host.addController(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
hostUpdated() {
|
hostUpdated() {
|
||||||
if (this._scrollContainer) {
|
if (!this.enabled || this._scrollContainer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this._attach();
|
||||||
|
}
|
||||||
|
|
||||||
|
hostDisconnected() {
|
||||||
|
this._detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _attach() {
|
||||||
this._scrollContainer = this._host.renderRoot?.querySelector(
|
this._scrollContainer = this._host.renderRoot?.querySelector(
|
||||||
this._selector
|
this._selector
|
||||||
);
|
);
|
||||||
@ -49,9 +78,18 @@ export class DragScrollController implements ReactiveController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hostDisconnected() {
|
private _detach() {
|
||||||
window.removeEventListener("mousemove", this._mouseMove);
|
window.removeEventListener("mousemove", this._mouseMove);
|
||||||
window.removeEventListener("mouseup", this._mouseUp);
|
window.removeEventListener("mouseup", this._mouseUp);
|
||||||
|
if (this._scrollContainer) {
|
||||||
|
this._scrollContainer.removeEventListener("mousedown", this._mouseDown);
|
||||||
|
this._scrollContainer = undefined;
|
||||||
|
}
|
||||||
|
this.scrolled = false;
|
||||||
|
this.scrolling = false;
|
||||||
|
this.mouseIsDown = false;
|
||||||
|
this.scrollStartX = 0;
|
||||||
|
this.scrollLeft = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _mouseDown = (event: MouseEvent) => {
|
private _mouseDown = (event: MouseEvent) => {
|
||||||
|
@ -600,12 +600,32 @@ export class HaChartBase extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getSeries() {
|
private _getSeries() {
|
||||||
if (!Array.isArray(this.data)) {
|
const series = ensureArray(this.data).filter(
|
||||||
return this.data;
|
|
||||||
}
|
|
||||||
return this.data.filter(
|
|
||||||
(d) => !this._hiddenDatasets.has(String(d.name ?? d.id))
|
(d) => !this._hiddenDatasets.has(String(d.name ?? d.id))
|
||||||
);
|
);
|
||||||
|
const yAxis = (this.options?.yAxis?.[0] ?? this.options?.yAxis) as
|
||||||
|
| YAXisOption
|
||||||
|
| undefined;
|
||||||
|
if (yAxis?.type === "log") {
|
||||||
|
// set <=0 values to null so they render as gaps on a log graph
|
||||||
|
return series.map((d) =>
|
||||||
|
d.type === "line"
|
||||||
|
? {
|
||||||
|
...d,
|
||||||
|
data: d.data?.map((v) =>
|
||||||
|
Array.isArray(v)
|
||||||
|
? [
|
||||||
|
v[0],
|
||||||
|
typeof v[1] !== "number" || v[1] > 0 ? v[1] : null,
|
||||||
|
...v.slice(2),
|
||||||
|
]
|
||||||
|
: v
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: d
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return series;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getDefaultHeight() {
|
private _getDefaultHeight() {
|
||||||
|
@ -85,7 +85,6 @@ class SearchInputOutlined extends LitElement {
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
/* For iOS */
|
/* For iOS */
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
--mdc-icon-button-size: 24px;
|
|
||||||
}
|
}
|
||||||
ha-outlined-text-field {
|
ha-outlined-text-field {
|
||||||
display: block;
|
display: block;
|
||||||
@ -94,6 +93,8 @@ class SearchInputOutlined extends LitElement {
|
|||||||
}
|
}
|
||||||
ha-svg-icon,
|
ha-svg-icon,
|
||||||
ha-icon-button {
|
ha-icon-button {
|
||||||
|
--mdc-icon-button-size: 24px;
|
||||||
|
height: var(--mdc-icon-button-size);
|
||||||
display: flex;
|
display: flex;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
|
@ -206,12 +206,29 @@ export class HuiViewBadges extends LitElement {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badges > * {
|
/* Use before and after because padding doesn't work well with scrolling */
|
||||||
min-width: fit-content;
|
.badges::before,
|
||||||
|
.badges::after {
|
||||||
|
content: "";
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
min-width: var(--badge-padding, 0px);
|
||||||
|
height: 16px;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.badges::before {
|
||||||
|
margin-left: -8px;
|
||||||
|
margin-inline-start: -8px;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
}
|
||||||
|
.badges::after {
|
||||||
|
margin-right: -8px;
|
||||||
|
margin-inline-end: -8px;
|
||||||
|
margin-inline-start: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badges > *:last-child:not(.add) {
|
.badges > * {
|
||||||
padding-right: var(--badges-padding-right, 0);
|
min-width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
hui-badge-edit-mode {
|
hui-badge-edit-mode {
|
||||||
|
@ -268,6 +268,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
|||||||
<div
|
<div
|
||||||
class="content ${classMap({
|
class="content ${classMap({
|
||||||
"has-header": !!this._config.title,
|
"has-header": !!this._config.title,
|
||||||
|
"has-rows": !!this._config.grid_options?.rows,
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
<statistics-chart
|
<statistics-chart
|
||||||
@ -389,6 +390,9 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
|||||||
statistics-chart {
|
statistics-chart {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.has-rows {
|
||||||
|
--chart-max-height: 100%;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ export class HuiViewHeader extends LitElement {
|
|||||||
|
|
||||||
private _dragScrollController = new DragScrollController(this, {
|
private _dragScrollController = new DragScrollController(this, {
|
||||||
selector: ".scroll",
|
selector: ".scroll",
|
||||||
|
enabled: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
connectedCallback(): void {
|
connectedCallback(): void {
|
||||||
@ -81,6 +82,11 @@ export class HuiViewHeader extends LitElement {
|
|||||||
this._checkHidden();
|
this._checkHidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (changedProperties.has("config") || changedProperties.has("lovelace")) {
|
||||||
|
this._dragScrollController.enabled =
|
||||||
|
!this.lovelace.editMode && this.config?.badges_wrap === "scroll";
|
||||||
|
}
|
||||||
|
|
||||||
if (changedProperties.has("config")) {
|
if (changedProperties.has("config")) {
|
||||||
if (this.config?.card) {
|
if (this.config?.card) {
|
||||||
this.card = this._createCardElement(this.config.card);
|
this.card = this._createCardElement(this.config.card);
|
||||||
@ -196,6 +202,9 @@ export class HuiViewHeader extends LitElement {
|
|||||||
this.config?.badges_position ?? DEFAULT_VIEW_HEADER_BADGES_POSITION;
|
this.config?.badges_position ?? DEFAULT_VIEW_HEADER_BADGES_POSITION;
|
||||||
const badgesWrap =
|
const badgesWrap =
|
||||||
this.config?.badges_wrap ?? DEFAULT_VIEW_HEADER_BADGES_WRAP;
|
this.config?.badges_wrap ?? DEFAULT_VIEW_HEADER_BADGES_WRAP;
|
||||||
|
const badgeDragging = this._dragScrollController.scrolling
|
||||||
|
? "dragging"
|
||||||
|
: "";
|
||||||
|
|
||||||
const hasHeading = card !== undefined;
|
const hasHeading = card !== undefined;
|
||||||
const hasBadges = this.badges.length > 0;
|
const hasBadges = this.badges.length > 0;
|
||||||
@ -258,10 +267,7 @@ export class HuiViewHeader extends LitElement {
|
|||||||
${this.lovelace && (editMode || this.badges.length > 0)
|
${this.lovelace && (editMode || this.badges.length > 0)
|
||||||
? html`
|
? html`
|
||||||
<div
|
<div
|
||||||
class="badges ${badgesPosition} ${badgesWrap} ${this
|
class="badges ${badgesPosition} ${badgesWrap} ${badgeDragging}"
|
||||||
._dragScrollController.scrolling
|
|
||||||
? "dragging"
|
|
||||||
: ""}"
|
|
||||||
>
|
>
|
||||||
<hui-view-badges
|
<hui-view-badges
|
||||||
.badges=${this.badges}
|
.badges=${this.badges}
|
||||||
@ -350,7 +356,7 @@ export class HuiViewHeader extends LitElement {
|
|||||||
|
|
||||||
.container:not(.edit-mode) .badges.scroll {
|
.container:not(.edit-mode) .badges.scroll {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
max-width: calc(100% - 16px);
|
max-width: 100%;
|
||||||
scrollbar-color: var(--scrollbar-thumb-color) transparent;
|
scrollbar-color: var(--scrollbar-thumb-color) transparent;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
mask-image: linear-gradient(
|
mask-image: linear-gradient(
|
||||||
@ -360,7 +366,6 @@ export class HuiViewHeader extends LitElement {
|
|||||||
black calc(100% - 16px),
|
black calc(100% - 16px),
|
||||||
transparent 100%
|
transparent 100%
|
||||||
);
|
);
|
||||||
padding-left: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hui-view-badges {
|
hui-view-badges {
|
||||||
@ -398,7 +403,7 @@ export class HuiViewHeader extends LitElement {
|
|||||||
.container:not(.edit-mode) .layout.badges-scroll hui-view-badges {
|
.container:not(.edit-mode) .layout.badges-scroll hui-view-badges {
|
||||||
--badges-wrap: nowrap;
|
--badges-wrap: nowrap;
|
||||||
--badges-aligmnent: flex-start;
|
--badges-aligmnent: flex-start;
|
||||||
--badges-padding-right: 16px;
|
--badge-padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container:not(.edit-mode) .layout.center.badges-scroll hui-view-badges {
|
.container:not(.edit-mode) .layout.center.badges-scroll hui-view-badges {
|
||||||
@ -419,7 +424,7 @@ export class HuiViewHeader extends LitElement {
|
|||||||
hui-view-badges {
|
hui-view-badges {
|
||||||
--badges-wrap: wrap;
|
--badges-wrap: wrap;
|
||||||
--badges-aligmnent: flex-end;
|
--badges-aligmnent: flex-end;
|
||||||
--badges-padding-right: 0;
|
--badge-padding: 0;
|
||||||
}
|
}
|
||||||
.layout.responsive.has-heading hui-view-badges {
|
.layout.responsive.has-heading hui-view-badges {
|
||||||
--badges-aligmnent: flex-end;
|
--badges-aligmnent: flex-end;
|
||||||
|
@ -6407,11 +6407,11 @@
|
|||||||
"title": "Network discovery",
|
"title": "Network discovery",
|
||||||
"description": "Explore what data Home Assistant can see on the network.",
|
"description": "Explore what data Home Assistant can see on the network.",
|
||||||
"dhcp": "DHCP browser",
|
"dhcp": "DHCP browser",
|
||||||
"dhcp_info": "Show devices detected by Home Assistant using methods like DHCP, ARP+PTR lookups, and router-based device trackers. DHCP (Dynamic Host Configuration Protocol) data is received when devices join the network and request an IP address.",
|
"dhcp_info": "Show devices detected using methods like DHCP, ARP+PTR lookups, and router-based device trackers.",
|
||||||
"ssdp": "SSDP browser",
|
"ssdp": "SSDP browser",
|
||||||
"ssdp_info": "Show devices discovered by Home Assistant using SSDP/UPnP. Devices that Home Assistant has discovered will appear here.",
|
"ssdp_info": "Show devices discovered using SSDP/UPnP.",
|
||||||
"zeroconf": "Zeroconf browser",
|
"zeroconf": "Zeroconf browser",
|
||||||
"zeroconf_info": "Show devices discovered by Home Assistant using mDNS. Only devices that Home Assistant is actively searching for will appear here."
|
"zeroconf_info": "Show services discovered using mDNS. Does not include services unknown to Home Assistant."
|
||||||
},
|
},
|
||||||
"network_adapter": "Network adapter",
|
"network_adapter": "Network adapter",
|
||||||
"network_adapter_info": "Configure which network adapters integrations will use. Currently this setting only affects multicast traffic. A restart is required for these settings to apply.",
|
"network_adapter_info": "Configure which network adapters integrations will use. Currently this setting only affects multicast traffic. A restart is required for these settings to apply.",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user