mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-13 03:09:26 +00:00
Compare commits
34 Commits
remove-pad
...
improve_ar
Author | SHA1 | Date | |
---|---|---|---|
![]() |
37b79c67c9 | ||
![]() |
0715835e0d | ||
![]() |
d6ebd9bfc4 | ||
![]() |
15ae37d077 | ||
![]() |
461d5eb687 | ||
![]() |
3058fcad46 | ||
![]() |
06bd1ae4cd | ||
![]() |
00733357a1 | ||
![]() |
665c971822 | ||
![]() |
eff5471dd1 | ||
![]() |
4fba9c3c0a | ||
![]() |
0b32b51e2f | ||
![]() |
6370b0b8e5 | ||
![]() |
681518f443 | ||
![]() |
9f5b89978d | ||
![]() |
130839ee7b | ||
![]() |
ba4ec960c8 | ||
![]() |
6692d9c6aa | ||
![]() |
4d2d94c54f | ||
![]() |
d59c6612c6 | ||
![]() |
498f158253 | ||
![]() |
b8026ccf46 | ||
![]() |
84def48222 | ||
![]() |
cea0ac02fe | ||
![]() |
e1b099e88b | ||
![]() |
d571ef3f18 | ||
![]() |
c8cffef647 | ||
![]() |
6b568307a4 | ||
![]() |
1b501907f1 | ||
![]() |
c7e79998a4 | ||
![]() |
03ccf014d9 | ||
![]() |
ab41bdb87d | ||
![]() |
821a0bc418 | ||
![]() |
6d931b9e37 |
@@ -1,18 +0,0 @@
|
||||
diff --git a/dist/hls.light.mjs b/dist/hls.light.mjs
|
||||
index eed9d788fafdb159975e1a2eb08ac88ba9c9ac33..ace881935e6665946f1c8110ebd2f739cde4427e 100644
|
||||
--- a/dist/hls.light.mjs
|
||||
+++ b/dist/hls.light.mjs
|
||||
@@ -20523,9 +20523,9 @@ class Hls {
|
||||
}
|
||||
Hls.defaultConfig = void 0;
|
||||
|
||||
-var KeySystemFormats = empty.KeySystemFormats;
|
||||
-var KeySystems = empty.KeySystems;
|
||||
-var SubtitleStreamController = empty.SubtitleStreamController;
|
||||
-var TimelineController = empty.TimelineController;
|
||||
+var KeySystemFormats = empty;
|
||||
+var KeySystems = empty;
|
||||
+var SubtitleStreamController = empty;
|
||||
+var TimelineController = empty;
|
||||
export { AbrController, AttrList, Cues as AudioStreamController, Cues as AudioTrackController, BasePlaylistController, BaseSegment, BaseStreamController, BufferController, Cues as CMCDController, CapLevelController, ChunkMetadata, ContentSteeringController, DateRange, Cues as EMEController, ErrorActionFlags, ErrorController, ErrorDetails, ErrorTypes, Events, FPSController, Fragment, Hls, HlsSkip, HlsUrlParameters, KeySystemFormats, KeySystems, Level, LevelDetails, LevelKey, LoadStats, MetadataSchema, NetworkErrorAction, Part, PlaylistLevelType, SubtitleStreamController, Cues as SubtitleTrackController, TimelineController, Hls as default, getMediaSource, isMSESupported, isSupported };
|
||||
//# sourceMappingURL=hls.light.mjs.map
|
@@ -302,7 +302,7 @@ export class HcConnect extends LitElement {
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
|
||||
.error a {
|
||||
|
@@ -86,7 +86,7 @@ class HcLayout extends LitElement {
|
||||
.card-header {
|
||||
color: var(--ha-card-header-color, var(--primary-text-color));
|
||||
font-family: var(--ha-card-header-font-family, inherit);
|
||||
font-size: var(--ha-card-header-font-size, 24px);
|
||||
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
||||
letter-spacing: -0.012em;
|
||||
line-height: 32px;
|
||||
padding: 24px 16px 16px;
|
||||
@@ -98,7 +98,7 @@ class HcLayout extends LitElement {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
color: var(--secondary-text-color);
|
||||
line-height: initial;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ class HcLayout extends LitElement {
|
||||
}
|
||||
|
||||
:host ::slotted(.section-header) {
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
padding: 4px 16px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@@ -135,7 +135,7 @@ class HcLayout extends LitElement {
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
font-size: var(--ha-font-size-s);
|
||||
padding: 8px 0 24px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
@@ -29,7 +29,7 @@ class HcLaunchScreen extends LitElement {
|
||||
display: block;
|
||||
height: 100vh;
|
||||
background-color: #f2f4f9;
|
||||
font-size: 24px;
|
||||
font-size: var(--ha-font-size-2xl);
|
||||
}
|
||||
.container {
|
||||
display: flex;
|
||||
|
@@ -42,7 +42,7 @@ class PageDescription extends HaMarkdown {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
line-height: 24px;
|
||||
}
|
||||
.root {
|
||||
|
@@ -34,7 +34,7 @@ class HaDemoOptions extends LitElement {
|
||||
height: 64px;
|
||||
padding: 0 16px;
|
||||
pointer-events: none;
|
||||
font-size: 20px;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -250,8 +250,8 @@ class HaGallery extends LitElement {
|
||||
}
|
||||
|
||||
.page-footer .header {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
font-size: var(--ha-font-size-l);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
}
|
||||
|
@@ -150,7 +150,7 @@ export class DemoHaBarButton extends LitElement {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.custom {
|
||||
--control-button-icon-color: var(--primary-color);
|
||||
|
@@ -86,7 +86,7 @@ export class DemoHarControlNumberButtons extends LitElement {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.custom {
|
||||
color: #2196f3;
|
||||
|
@@ -125,7 +125,7 @@ export class DemoHaControlSelectMenu extends LitElement {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.custom {
|
||||
--control-button-icon-color: var(--primary-color);
|
||||
|
@@ -181,7 +181,7 @@ export class DemoHaControlSelect extends LitElement {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.custom {
|
||||
--mdc-icon-size: 24px;
|
||||
|
@@ -144,7 +144,7 @@ export class DemoHaBarSlider extends LitElement {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.custom {
|
||||
--control-slider-color: #ffcf4c;
|
||||
|
@@ -112,7 +112,7 @@ export class DemoHaControlSwitch extends LitElement {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.custom {
|
||||
--control-switch-on-color: var(--green-color);
|
||||
|
@@ -105,8 +105,8 @@ export class DemoHaHsColorPicker extends LitElement {
|
||||
width: 400px;
|
||||
}
|
||||
.value {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
`;
|
||||
|
@@ -123,7 +123,7 @@ export class DemoHaSelectBox extends LitElement {
|
||||
margin: 0;
|
||||
}
|
||||
label {
|
||||
font-weight: 600;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
margin-bottom: 8px;
|
||||
display: block;
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { html, css, LitElement } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
||||
import "../../../../src/components/ha-bar";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-spinner";
|
||||
@@ -11,29 +12,66 @@ export class DemoHaSpinner extends LitElement {
|
||||
@property({ attribute: false }) hass!: HomeAssistant;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`<ha-card header="Basic spinner">
|
||||
<div class="card-content">
|
||||
<ha-spinner></ha-spinner></div
|
||||
></ha-card>
|
||||
<ha-card header="Different spinner sizes">
|
||||
<div class="card-content">
|
||||
<ha-spinner size="tiny"></ha-spinner>
|
||||
<ha-spinner size="small"></ha-spinner>
|
||||
<ha-spinner size="medium"></ha-spinner>
|
||||
<ha-spinner size="large"></ha-spinner></div
|
||||
></ha-card>
|
||||
<ha-card header="Spinner with an aria-label">
|
||||
<div class="card-content">
|
||||
<ha-spinner aria-label="Doing something..."></ha-spinner>
|
||||
<ha-spinner .ariaLabel=${"Doing something..."}></ha-spinner></div
|
||||
></ha-card>`;
|
||||
return html`
|
||||
${["light", "dark"].map(
|
||||
(mode) => html`
|
||||
<div class=${mode}>
|
||||
<ha-card header="ha-badge ${mode} demo">
|
||||
<div class="card-content">
|
||||
<ha-spinner></ha-spinner>
|
||||
<ha-spinner size="tiny"></ha-spinner>
|
||||
<ha-spinner size="small"></ha-spinner>
|
||||
<ha-spinner size="medium"></ha-spinner>
|
||||
<ha-spinner size="large"></ha-spinner>
|
||||
<ha-spinner aria-label="Doing something..."></ha-spinner>
|
||||
<ha-spinner .ariaLabel=${"Doing something..."}></ha-spinner>
|
||||
</div>
|
||||
</ha-card>
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
applyThemesOnElement(
|
||||
this.shadowRoot!.querySelector(".dark"),
|
||||
{
|
||||
default_theme: "default",
|
||||
default_dark_theme: "default",
|
||||
themes: {},
|
||||
darkMode: true,
|
||||
theme: "default",
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.dark,
|
||||
.light {
|
||||
display: block;
|
||||
background-color: var(--primary-background-color);
|
||||
padding: 0 50px;
|
||||
margin: 16px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
@@ -106,7 +106,7 @@ export class DemoDateTimeDateTimeNumeric extends LitElement {
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -106,7 +106,7 @@ export class DemoDateTimeDateTimeSeconds extends LitElement {
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -106,7 +106,7 @@ export class DemoDateTimeDateTimeShortYear extends LitElement {
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -106,7 +106,7 @@ export class DemoDateTimeDateTimeShort extends LitElement {
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -106,7 +106,7 @@ export class DemoDateTimeDateTime extends LitElement {
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -92,7 +92,7 @@ export class DemoDateTimeDate extends LitElement {
|
||||
|
||||
static styles = css`
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -106,7 +106,7 @@ export class DemoDateTimeTimeSeconds extends LitElement {
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -106,7 +106,7 @@ export class DemoDateTimeTimeWeekday extends LitElement {
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -106,7 +106,7 @@ export class DemoDateTimeTime extends LitElement {
|
||||
margin: 12px auto;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
|
@@ -428,13 +428,13 @@ class HassioAddonConfig extends LitElement {
|
||||
.header h2 {
|
||||
color: var(--ha-card-header-color, var(--primary-text-color));
|
||||
font-family: var(--ha-card-header-font-family, inherit);
|
||||
font-size: var(--ha-card-header-font-size, 24px);
|
||||
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
||||
letter-spacing: -0.012em;
|
||||
line-height: 48px;
|
||||
padding: 12px 16px 16px;
|
||||
display: block;
|
||||
margin-block: 0px;
|
||||
font-weight: normal;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
}
|
||||
.card-actions.right {
|
||||
justify-content: flex-end;
|
||||
|
@@ -1280,12 +1280,12 @@ class HassioAddonInfo extends LitElement {
|
||||
padding-left: 8px;
|
||||
padding-inline-start: 8px;
|
||||
padding-inline-end: initial;
|
||||
font-size: 24px;
|
||||
font-size: var(--ha-font-size-2xl);
|
||||
color: var(--ha-card-header-color, var(--primary-text-color));
|
||||
}
|
||||
.addon-version {
|
||||
float: var(--float-end);
|
||||
font-size: 15px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
vertical-align: middle;
|
||||
}
|
||||
.errors {
|
||||
|
@@ -391,7 +391,7 @@ export class HassioBackups extends LitElement {
|
||||
top: -4px;
|
||||
}
|
||||
.selected-txt {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
padding-left: 16px;
|
||||
padding-inline-start: 16px;
|
||||
padding-inline-end: initial;
|
||||
@@ -401,7 +401,7 @@ export class HassioBackups extends LitElement {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.header-toolbar .selected-txt {
|
||||
font-size: 16px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
}
|
||||
.header-toolbar .header-btns {
|
||||
margin-right: -12px;
|
||||
|
@@ -131,7 +131,7 @@ export class HassioUpdate extends LitElement {
|
||||
}
|
||||
.update-heading {
|
||||
font-size: var(--ha-font-size-l);
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
margin-bottom: 0.5em;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
@@ -173,7 +173,7 @@ class HassioHardwareDialog extends LitElement {
|
||||
font-family: var(--ha-font-family-code);
|
||||
}
|
||||
code {
|
||||
font-size: 85%;
|
||||
font-size: var(--ha-font-size-s);
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
search-input {
|
||||
|
@@ -1,3 +1,8 @@
|
||||
import {
|
||||
haFontFamilyBody,
|
||||
haFontSmoothing,
|
||||
haMozOsxFontSmoothing,
|
||||
} from "../../src/resources/theme/typography.globals";
|
||||
import "./hassio-main";
|
||||
|
||||
import("../../src/resources/append-ha-style");
|
||||
@@ -5,10 +10,10 @@ import("../../src/resources/append-ha-style");
|
||||
const styleEl = document.createElement("style");
|
||||
styleEl.textContent = `
|
||||
body {
|
||||
font-family: Roboto, sans-serif;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-weight: 400;
|
||||
font-family: ${haFontFamilyBody};
|
||||
-moz-osx-font-smoothing: ${haMozOsxFontSmoothing};
|
||||
-webkit-font-smoothing: ${haFontSmoothing};
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
|
@@ -340,12 +340,12 @@ class HassioIngressView extends LitElement {
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
pointer-events: none;
|
||||
background-color: var(--app-header-background-color);
|
||||
font-weight: 400;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
color: var(--app-header-text-color, white);
|
||||
border-bottom: var(--app-header-border-bottom, none);
|
||||
box-sizing: border-box;
|
||||
|
@@ -14,6 +14,7 @@ export const hassioStyle = css`
|
||||
margin-bottom: 8px;
|
||||
font-family: var(--ha-font-family-body);
|
||||
-webkit-font-smoothing: var(--ha-font-smoothing);
|
||||
-moz-osx-font-smoothing: var(--ha-moz-osx-font-smoothing);
|
||||
font-size: var(--ha-font-size-2xl);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: var(--ha-line-height-condensed);
|
||||
|
16
package.json
16
package.json
@@ -111,7 +111,7 @@
|
||||
"fuse.js": "7.1.0",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"gulp-zopfli-green": "6.0.2",
|
||||
"hls.js": "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch",
|
||||
"hls.js": "1.6.2",
|
||||
"home-assistant-js-websocket": "9.5.0",
|
||||
"idb-keyval": "6.2.1",
|
||||
"intl-messageformat": "10.7.16",
|
||||
@@ -155,11 +155,11 @@
|
||||
"@babel/plugin-transform-runtime": "7.27.1",
|
||||
"@babel/preset-env": "7.27.1",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.20.0",
|
||||
"@lokalise/node-api": "14.5.0",
|
||||
"@lokalise/node-api": "14.5.2",
|
||||
"@octokit/auth-oauth-device": "7.1.5",
|
||||
"@octokit/plugin-retry": "7.2.1",
|
||||
"@octokit/rest": "21.1.1",
|
||||
"@rsdoctor/rspack-plugin": "1.0.2",
|
||||
"@rsdoctor/rspack-plugin": "1.1.0",
|
||||
"@rspack/cli": "1.3.8",
|
||||
"@rspack/core": "1.3.8",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
@@ -170,7 +170,7 @@
|
||||
"@types/html-minifier-terser": "7.0.2",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/leaflet": "1.9.17",
|
||||
"@types/leaflet-draw": "1.0.11",
|
||||
"@types/leaflet-draw": "1.0.12",
|
||||
"@types/leaflet.markercluster": "1.5.5",
|
||||
"@types/lodash.merge": "4.6.9",
|
||||
"@types/luxon": "3.6.2",
|
||||
@@ -180,7 +180,7 @@
|
||||
"@types/tar": "6.1.13",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@vitest/coverage-v8": "3.1.2",
|
||||
"@vitest/coverage-v8": "3.1.3",
|
||||
"babel-loader": "10.0.0",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"browserslist-useragent-regexp": "4.1.3",
|
||||
@@ -205,7 +205,7 @@
|
||||
"husky": "9.1.7",
|
||||
"jsdom": "26.1.0",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "15.5.1",
|
||||
"lint-staged": "15.5.2",
|
||||
"lit-analyzer": "2.0.3",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.template": "4.5.0",
|
||||
@@ -219,9 +219,9 @@
|
||||
"terser-webpack-plugin": "5.3.14",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "8.31.1",
|
||||
"typescript-eslint": "8.32.0",
|
||||
"vite-tsconfig-paths": "5.1.4",
|
||||
"vitest": "3.1.2",
|
||||
"vitest": "3.1.3",
|
||||
"webpack-stats-plugin": "1.1.3",
|
||||
"webpackbar": "7.0.0",
|
||||
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
||||
|
@@ -93,7 +93,7 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
background-color: var(--primary-background-color, #fafafa);
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
line-height: 20px;
|
||||
}
|
||||
.card-content {
|
||||
@@ -151,8 +151,8 @@ export class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
|
||||
margin-inline-start: initial;
|
||||
}
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 400;
|
||||
font-size: var(--ha-font-size-3xl);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
@@ -57,8 +57,8 @@ export class HaPickAuthProvider extends LitElement {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-size: var(--ha-font-size-m);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: 20px;
|
||||
}
|
||||
h3:before {
|
||||
|
@@ -10,7 +10,6 @@ import type { LitElement } from "lit";
|
||||
export interface DragScrollControllerConfig {
|
||||
selector: string;
|
||||
enabled?: boolean;
|
||||
trackScroll?: boolean;
|
||||
}
|
||||
|
||||
export class DragScrollController implements ReactiveController {
|
||||
@@ -24,10 +23,6 @@ export class DragScrollController implements ReactiveController {
|
||||
|
||||
public scrollLeft = 0;
|
||||
|
||||
public scrolledStart = false;
|
||||
|
||||
public scrolledEnd = false;
|
||||
|
||||
private _host: ReactiveControllerHost & LitElement;
|
||||
|
||||
private _selector: string;
|
||||
@@ -36,8 +31,6 @@ export class DragScrollController implements ReactiveController {
|
||||
|
||||
private _enabled = true;
|
||||
|
||||
private _trackScroll = false;
|
||||
|
||||
public get enabled(): boolean {
|
||||
return this._enabled;
|
||||
}
|
||||
@@ -57,11 +50,10 @@ export class DragScrollController implements ReactiveController {
|
||||
|
||||
constructor(
|
||||
host: ReactiveControllerHost & LitElement,
|
||||
{ selector, enabled, trackScroll }: DragScrollControllerConfig
|
||||
{ selector, enabled }: DragScrollControllerConfig
|
||||
) {
|
||||
this._selector = selector;
|
||||
this._host = host;
|
||||
this._trackScroll = trackScroll ?? false;
|
||||
this.enabled = enabled ?? true;
|
||||
host.addController(this);
|
||||
}
|
||||
@@ -83,14 +75,6 @@ export class DragScrollController implements ReactiveController {
|
||||
);
|
||||
if (this._scrollContainer) {
|
||||
this._scrollContainer.addEventListener("mousedown", this._mouseDown);
|
||||
if (this._trackScroll) {
|
||||
this._scrollContainer.addEventListener("scroll", this._onScroll);
|
||||
this.scrolledStart = this._scrollContainer.scrollLeft > 0;
|
||||
this.scrolledEnd =
|
||||
this._scrollContainer.scrollLeft + this._scrollContainer.offsetWidth <
|
||||
this._scrollContainer.scrollWidth;
|
||||
this._host.requestUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,34 +83,15 @@ export class DragScrollController implements ReactiveController {
|
||||
window.removeEventListener("mouseup", this._mouseUp);
|
||||
if (this._scrollContainer) {
|
||||
this._scrollContainer.removeEventListener("mousedown", this._mouseDown);
|
||||
this._scrollContainer.removeEventListener("scroll", this._onScroll);
|
||||
this._scrollContainer = undefined;
|
||||
}
|
||||
this.scrolled = false;
|
||||
this.scrolling = false;
|
||||
this.scrolledStart = false;
|
||||
this.scrolledEnd = false;
|
||||
this.mouseIsDown = false;
|
||||
this.scrollStartX = 0;
|
||||
this.scrollLeft = 0;
|
||||
}
|
||||
|
||||
private _onScroll = (event: Event) => {
|
||||
const oldScrolledStart = this.scrolledStart;
|
||||
const oldScrolledEnd = this.scrolledEnd;
|
||||
|
||||
const container = event.currentTarget as HTMLElement;
|
||||
this.scrolledStart = container.scrollLeft > 0;
|
||||
this.scrolledEnd =
|
||||
container.scrollLeft + container.offsetWidth < container.scrollWidth;
|
||||
if (
|
||||
this.scrolledStart !== oldScrolledStart ||
|
||||
this.scrolledEnd !== oldScrolledEnd
|
||||
) {
|
||||
this._host.requestUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
private _mouseDown = (event: MouseEvent) => {
|
||||
const scrollContainer = this._scrollContainer;
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import type { CSSResult } from "lit";
|
||||
|
||||
const _extractCssVars = (
|
||||
cssString: string,
|
||||
condition: (string) => boolean = () => true
|
||||
condition: (string: string) => boolean = () => true
|
||||
) => {
|
||||
const variables: Record<string, string> = {};
|
||||
|
||||
|
@@ -739,7 +739,7 @@ export class HaChartBase extends LitElement {
|
||||
max-height: 60%;
|
||||
overflow-y: auto;
|
||||
padding: 12px 0 0;
|
||||
font-size: 12px;
|
||||
font-size: var(--ha-font-size-s);
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.chart-legend ul {
|
||||
|
@@ -105,10 +105,41 @@ export class HaSankeyChart extends LitElement {
|
||||
|
||||
private _createData = memoizeOne((data: SankeyChartData, width = 0) => {
|
||||
const filteredNodes = data.nodes.filter((n) => n.value > 0);
|
||||
const indexes = [...new Set(filteredNodes.map((n) => n.index))];
|
||||
const indexes = [...new Set(filteredNodes.map((n) => n.index))].sort();
|
||||
const depthMap = new Map<number, number>();
|
||||
indexes.sort().forEach((index, i) => {
|
||||
const sections: Node[][] = [];
|
||||
indexes.forEach((index, i) => {
|
||||
depthMap.set(index, i);
|
||||
const nodesWithIndex = filteredNodes.filter((n) => n.index === index);
|
||||
if (nodesWithIndex.length > 0) {
|
||||
sections.push(
|
||||
sections.length > 0
|
||||
? nodesWithIndex.sort((a, b) => {
|
||||
// sort by the order of their parents in the previous section with orphans at the end
|
||||
const aParentIndex = this._findParentIndex(
|
||||
a.id,
|
||||
data.links,
|
||||
sections
|
||||
);
|
||||
const bParentIndex = this._findParentIndex(
|
||||
b.id,
|
||||
data.links,
|
||||
sections
|
||||
);
|
||||
if (aParentIndex === bParentIndex) {
|
||||
return 0;
|
||||
}
|
||||
if (aParentIndex === -1) {
|
||||
return 1;
|
||||
}
|
||||
if (bParentIndex === -1) {
|
||||
return -1;
|
||||
}
|
||||
return aParentIndex - bParentIndex;
|
||||
})
|
||||
: nodesWithIndex
|
||||
);
|
||||
}
|
||||
});
|
||||
const links = this._processLinks(filteredNodes, data.links);
|
||||
const sectionWidth = width / indexes.length;
|
||||
@@ -117,7 +148,7 @@ export class HaSankeyChart extends LitElement {
|
||||
return {
|
||||
id: "sankey",
|
||||
type: "sankey",
|
||||
nodes: filteredNodes.map((node) => ({
|
||||
nodes: sections.flat().map((node) => ({
|
||||
id: node.id,
|
||||
value: node.value,
|
||||
itemStyle: {
|
||||
@@ -227,6 +258,23 @@ export class HaSankeyChart extends LitElement {
|
||||
return links;
|
||||
}
|
||||
|
||||
private _findParentIndex(id: string, links: Link[], sections: Node[][]) {
|
||||
const parent = links.find((l) => l.target === id)?.source;
|
||||
if (!parent) {
|
||||
return -1;
|
||||
}
|
||||
let offset = 0;
|
||||
for (let i = sections.length - 1; i >= 0; i--) {
|
||||
const section = sections[i];
|
||||
const index = section.findIndex((n) => n.id === parent);
|
||||
if (index !== -1) {
|
||||
return offset + index;
|
||||
}
|
||||
offset += section.length;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
|
@@ -60,7 +60,7 @@ export class HaAssistChip extends AssistChip {
|
||||
opacity: var(--ha-assist-chip-active-container-opacity);
|
||||
}
|
||||
.label {
|
||||
font-family: Roboto, sans-serif;
|
||||
font-family: var(--ha-font-family-body);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -928,12 +928,12 @@ export class HaDataTable extends LitElement {
|
||||
height: 100%;
|
||||
}
|
||||
.mdc-data-table__content {
|
||||
font-family: Roboto, sans-serif;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: var(--ha-font-family-body);
|
||||
-moz-osx-font-smoothing: var(--ha-moz-osx-font-smoothing);
|
||||
-webkit-font-smoothing: var(--ha-font-smoothing);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 400;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
letter-spacing: 0.0178571429em;
|
||||
text-decoration: inherit;
|
||||
text-transform: inherit;
|
||||
@@ -1048,12 +1048,12 @@ export class HaDataTable extends LitElement {
|
||||
}
|
||||
|
||||
.mdc-data-table__cell {
|
||||
font-family: Roboto, sans-serif;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: var(--ha-font-family-body);
|
||||
-moz-osx-font-smoothing: var(--ha-moz-osx-font-smoothing);
|
||||
-webkit-font-smoothing: var(--ha-font-smoothing);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
font-weight: 400;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
letter-spacing: 0.0178571429em;
|
||||
text-decoration: inherit;
|
||||
text-transform: inherit;
|
||||
@@ -1170,12 +1170,12 @@ export class HaDataTable extends LitElement {
|
||||
}
|
||||
|
||||
.mdc-data-table__header-cell {
|
||||
font-family: Roboto, sans-serif;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: var(--ha-font-family-body);
|
||||
-moz-osx-font-smoothing: var(--ha-moz-osx-font-smoothing);
|
||||
-webkit-font-smoothing: var(--ha-font-smoothing);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.375rem;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
letter-spacing: 0.0071428571em;
|
||||
text-decoration: inherit;
|
||||
text-transform: inherit;
|
||||
@@ -1199,7 +1199,7 @@ export class HaDataTable extends LitElement {
|
||||
padding-inline-start: 12px;
|
||||
padding-inline-end: initial;
|
||||
width: 100%;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
|
@@ -267,7 +267,7 @@ export class HaStateLabelBadge extends LitElement {
|
||||
cursor: pointer;
|
||||
}
|
||||
.big {
|
||||
font-size: 70%;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
}
|
||||
ha-label-badge {
|
||||
--ha-label-badge-color: var(--label-badge-red);
|
||||
|
@@ -129,7 +129,7 @@ class HaAlert extends LitElement {
|
||||
}
|
||||
.title {
|
||||
margin-top: 2px;
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.action mwc-button,
|
||||
.action ha-icon-button {
|
||||
|
@@ -56,7 +56,7 @@ export class HaAnsiToHtml extends LitElement {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
.italic {
|
||||
font-style: italic;
|
||||
|
508
src/components/ha-area-combo-box.ts
Normal file
508
src/components/ha-area-combo-box.ts
Normal file
@@ -0,0 +1,508 @@
|
||||
import { mdiTextureBox } from "@mdi/js";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import Fuse from "fuse.js";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeAreaName } from "../common/entity/compute_area_name";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import { computeFloorName } from "../common/entity/compute_floor_name";
|
||||
import { getAreaContext } from "../common/entity/context/get_area_context";
|
||||
import { caseInsensitiveStringCompare } from "../common/string/compare";
|
||||
import type { AreaRegistryEntry } from "../data/area_registry";
|
||||
import { createAreaRegistryEntry } from "../data/area_registry";
|
||||
import type {
|
||||
DeviceEntityDisplayLookup,
|
||||
DeviceRegistryEntry,
|
||||
} from "../data/device_registry";
|
||||
import { getDeviceEntityDisplayLookup } from "../data/device_registry";
|
||||
import type { EntityRegistryDisplayEntry } from "../data/entity_registry";
|
||||
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
||||
import { showAreaRegistryDetailDialog } from "../panels/config/areas/show-dialog-area-registry-detail";
|
||||
import { HaFuse } from "../resources/fuse";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-combo-box-item";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
interface AreaComboBoxItem {
|
||||
// Force empty label to always display empty value by default in the search field
|
||||
id: string;
|
||||
label: "";
|
||||
primary: string;
|
||||
secondary?: string;
|
||||
icon?: string;
|
||||
search_labels?: string[];
|
||||
sorting_label?: string;
|
||||
}
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<AreaComboBoxItem> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
${item.icon
|
||||
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
||||
: html`<ha-svg-icon slot="start" .path=${mdiTextureBox}></ha-svg-icon>`}
|
||||
<span slot="headline">${item.primary}</span>
|
||||
${item.secondary
|
||||
? html`<span slot="supporting-text">${item.secondary}</span>`
|
||||
: nothing}
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
const ADD_NEW_ID = "___ADD_NEW___";
|
||||
const NO_ITEMS_ID = "___NO_ITEMS___";
|
||||
const ADD_NEW_SUGGESTION_ID = "___ADD_NEW_SUGGESTION___";
|
||||
|
||||
@customElement("ha-area-combo-box")
|
||||
export class HaAreaComboBox extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public value?: string;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property() public placeholder?: string;
|
||||
|
||||
@property({ type: Boolean, attribute: "no-add" })
|
||||
public noAdd = false;
|
||||
|
||||
/**
|
||||
* Show only areas with entities from specific domains.
|
||||
* @type {Array}
|
||||
* @attr include-domains
|
||||
*/
|
||||
@property({ type: Array, attribute: "include-domains" })
|
||||
public includeDomains?: string[];
|
||||
|
||||
/**
|
||||
* Show no areas with entities of these domains.
|
||||
* @type {Array}
|
||||
* @attr exclude-domains
|
||||
*/
|
||||
@property({ type: Array, attribute: "exclude-domains" })
|
||||
public excludeDomains?: string[];
|
||||
|
||||
/**
|
||||
* Show only areas with entities of these device classes.
|
||||
* @type {Array}
|
||||
* @attr include-device-classes
|
||||
*/
|
||||
@property({ type: Array, attribute: "include-device-classes" })
|
||||
public includeDeviceClasses?: string[];
|
||||
|
||||
/**
|
||||
* List of areas to be excluded.
|
||||
* @type {Array}
|
||||
* @attr exclude-areas
|
||||
*/
|
||||
@property({ type: Array, attribute: "exclude-areas" })
|
||||
public excludeAreas?: string[];
|
||||
|
||||
@property({ attribute: false })
|
||||
public deviceFilter?: HaDevicePickerDeviceFilterFunc;
|
||||
|
||||
@property({ attribute: false })
|
||||
public entityFilter?: (entity: HassEntity) => boolean;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public required = false;
|
||||
|
||||
@state() private _opened?: boolean;
|
||||
|
||||
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
||||
|
||||
private _suggestion?: string;
|
||||
|
||||
private _init = false;
|
||||
|
||||
public async open() {
|
||||
await this.updateComplete;
|
||||
await this.comboBox?.open();
|
||||
}
|
||||
|
||||
public async focus() {
|
||||
await this.updateComplete;
|
||||
await this.comboBox?.focus();
|
||||
}
|
||||
|
||||
private _getItems = memoizeOne(
|
||||
(
|
||||
areas: AreaRegistryEntry[],
|
||||
devices: DeviceRegistryEntry[],
|
||||
entities: EntityRegistryDisplayEntry[],
|
||||
includeDomains: this["includeDomains"],
|
||||
excludeDomains: this["excludeDomains"],
|
||||
includeDeviceClasses: this["includeDeviceClasses"],
|
||||
deviceFilter: this["deviceFilter"],
|
||||
entityFilter: this["entityFilter"],
|
||||
noAdd: this["noAdd"],
|
||||
excludeAreas: this["excludeAreas"]
|
||||
): AreaComboBoxItem[] => {
|
||||
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
|
||||
let inputDevices: DeviceRegistryEntry[] | undefined;
|
||||
let inputEntities: EntityRegistryDisplayEntry[] | undefined;
|
||||
|
||||
if (
|
||||
includeDomains ||
|
||||
excludeDomains ||
|
||||
includeDeviceClasses ||
|
||||
deviceFilter ||
|
||||
entityFilter
|
||||
) {
|
||||
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
|
||||
inputDevices = devices;
|
||||
inputEntities = entities.filter((entity) => entity.area_id);
|
||||
|
||||
if (includeDomains) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) =>
|
||||
includeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) =>
|
||||
includeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
}
|
||||
|
||||
if (excludeDomains) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return true;
|
||||
}
|
||||
return entities.every(
|
||||
(entity) =>
|
||||
!excludeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
});
|
||||
inputEntities = inputEntities!.filter(
|
||||
(entity) =>
|
||||
!excludeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
}
|
||||
|
||||
if (includeDeviceClasses) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||
);
|
||||
});
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
return (
|
||||
stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (deviceFilter) {
|
||||
inputDevices = inputDevices!.filter((device) =>
|
||||
deviceFilter!(device)
|
||||
);
|
||||
}
|
||||
|
||||
if (entityFilter) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return entityFilter(stateObj);
|
||||
});
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return entityFilter!(stateObj);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let outputAreas = areas;
|
||||
|
||||
let areaIds: string[] | undefined;
|
||||
|
||||
if (inputDevices) {
|
||||
areaIds = inputDevices
|
||||
.filter((device) => device.area_id)
|
||||
.map((device) => device.area_id!);
|
||||
}
|
||||
|
||||
if (inputEntities) {
|
||||
areaIds = (areaIds ?? []).concat(
|
||||
inputEntities
|
||||
.filter((entity) => entity.area_id)
|
||||
.map((entity) => entity.area_id!)
|
||||
);
|
||||
}
|
||||
|
||||
if (areaIds) {
|
||||
outputAreas = outputAreas.filter((area) =>
|
||||
areaIds!.includes(area.area_id)
|
||||
);
|
||||
}
|
||||
|
||||
if (excludeAreas) {
|
||||
outputAreas = outputAreas.filter(
|
||||
(area) => !excludeAreas!.includes(area.area_id)
|
||||
);
|
||||
}
|
||||
|
||||
let items = outputAreas
|
||||
.map<AreaComboBoxItem>((area) => {
|
||||
const { floor } = getAreaContext(area, this.hass);
|
||||
const floorName = floor ? computeFloorName(floor) : undefined;
|
||||
const areaName = computeAreaName(area);
|
||||
return {
|
||||
label: "",
|
||||
id: area.area_id,
|
||||
primary: areaName || area.area_id,
|
||||
secondary: floorName,
|
||||
icon: area.icon || undefined,
|
||||
sorting_label: areaName,
|
||||
search_labels: [
|
||||
areaName,
|
||||
floorName,
|
||||
area.area_id,
|
||||
...area.aliases,
|
||||
].filter((v): v is string => Boolean(v)),
|
||||
};
|
||||
})
|
||||
.sort((entityA, entityB) =>
|
||||
caseInsensitiveStringCompare(
|
||||
entityA.sorting_label!,
|
||||
entityB.sorting_label!,
|
||||
this.hass.locale.language
|
||||
)
|
||||
);
|
||||
|
||||
if (!items.length) {
|
||||
items = [
|
||||
{
|
||||
label: "",
|
||||
id: NO_ITEMS_ID,
|
||||
primary: this.hass.localize("ui.components.area-picker.no_areas"),
|
||||
icon: "mdi:magnify",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return noAdd
|
||||
? items
|
||||
: [
|
||||
...items,
|
||||
{
|
||||
label: "",
|
||||
id: ADD_NEW_ID,
|
||||
primary: this.hass.localize("ui.components.area-picker.add_new"),
|
||||
icon: "mdi:plus",
|
||||
},
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
if (
|
||||
(!this._init && this.hass) ||
|
||||
(this._init && changedProps.has("_opened") && this._opened)
|
||||
) {
|
||||
this._init = true;
|
||||
const items = this._getItems(
|
||||
Object.values(this.hass.areas),
|
||||
Object.values(this.hass.devices),
|
||||
Object.values(this.hass.entities),
|
||||
this.includeDomains,
|
||||
this.excludeDomains,
|
||||
this.includeDeviceClasses,
|
||||
this.deviceFilter,
|
||||
this.entityFilter,
|
||||
this.noAdd,
|
||||
this.excludeAreas
|
||||
);
|
||||
this.comboBox.items = items;
|
||||
this.comboBox.filteredItems = items;
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-combo-box
|
||||
item-id-path="id"
|
||||
item-value-path="id"
|
||||
item-label-path="label"
|
||||
.hass=${this.hass}
|
||||
.helper=${this.helper}
|
||||
.value=${this._value}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
.label=${this.label === undefined && this.hass
|
||||
? this.hass.localize("ui.components.area-picker.area")
|
||||
: this.label}
|
||||
.placeholder=${this.placeholder
|
||||
? this.hass.areas[this.placeholder]?.name
|
||||
: undefined}
|
||||
.renderer=${rowRenderer}
|
||||
@filter-changed=${this._filterChanged}
|
||||
@opened-changed=${this._openedChanged}
|
||||
@value-changed=${this._areaChanged}
|
||||
>
|
||||
</ha-combo-box>
|
||||
`;
|
||||
}
|
||||
|
||||
private _fuseIndex = memoizeOne((items: AreaComboBoxItem[]) =>
|
||||
Fuse.createIndex(["search_labels"], items)
|
||||
);
|
||||
|
||||
private _filterChanged(ev: CustomEvent): void {
|
||||
if (!this._opened) return;
|
||||
|
||||
const target = ev.target as HaComboBox;
|
||||
const items = target.items as AreaComboBoxItem[];
|
||||
const filterString = ev.detail.value.trim().toLowerCase() as string;
|
||||
|
||||
const index = this._fuseIndex(items);
|
||||
const fuse = new HaFuse(items, {}, index);
|
||||
|
||||
const results = fuse.multiTermsSearch(filterString);
|
||||
|
||||
if (results) {
|
||||
if (results.length === 0) {
|
||||
if (this.noAdd) {
|
||||
this.comboBox.filteredItems = [
|
||||
{
|
||||
id: NO_ITEMS_ID,
|
||||
primary: this.hass.localize("ui.components.area-picker.no_match"),
|
||||
icon: "mdi:magnify",
|
||||
},
|
||||
] as AreaComboBoxItem[];
|
||||
} else {
|
||||
this._suggestion = filterString;
|
||||
this.comboBox.filteredItems = [
|
||||
{
|
||||
id: ADD_NEW_SUGGESTION_ID,
|
||||
primary: this.hass.localize(
|
||||
"ui.components.area-picker.add_new_sugestion",
|
||||
{ name: this._suggestion }
|
||||
),
|
||||
icon: "mdi:plus",
|
||||
},
|
||||
] as AreaComboBoxItem[];
|
||||
}
|
||||
} else {
|
||||
target.filteredItems = results.map((result) => result.item);
|
||||
}
|
||||
} else {
|
||||
this.comboBox.filteredItems = target.items as AreaComboBoxItem[];
|
||||
}
|
||||
}
|
||||
|
||||
private get _value() {
|
||||
return this.value || "";
|
||||
}
|
||||
|
||||
private _openedChanged(ev: ValueChangedEvent<boolean>) {
|
||||
this._opened = ev.detail.value;
|
||||
}
|
||||
|
||||
private _areaChanged(ev: ValueChangedEvent<string>) {
|
||||
ev.stopPropagation();
|
||||
let newValue = ev.detail.value;
|
||||
|
||||
if (newValue === NO_ITEMS_ID) {
|
||||
newValue = "";
|
||||
this.comboBox.setInputValue("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (![ADD_NEW_SUGGESTION_ID, ADD_NEW_ID].includes(newValue)) {
|
||||
if (newValue !== this._value) {
|
||||
this._setValue(newValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
(ev.target as any).value = this._value;
|
||||
|
||||
this.hass.loadFragmentTranslation("config");
|
||||
|
||||
showAreaRegistryDetailDialog(this, {
|
||||
suggestedName: newValue === ADD_NEW_SUGGESTION_ID ? this._suggestion : "",
|
||||
createEntry: async (values) => {
|
||||
try {
|
||||
const area = await createAreaRegistryEntry(this.hass, values);
|
||||
const areas = [...Object.values(this.hass.areas), area];
|
||||
this.comboBox.filteredItems = this._getItems(
|
||||
areas,
|
||||
Object.values(this.hass.devices)!,
|
||||
Object.values(this.hass.entities)!,
|
||||
this.includeDomains,
|
||||
this.excludeDomains,
|
||||
this.includeDeviceClasses,
|
||||
this.deviceFilter,
|
||||
this.entityFilter,
|
||||
this.noAdd,
|
||||
this.excludeAreas
|
||||
);
|
||||
await this.updateComplete;
|
||||
await this.comboBox.updateComplete;
|
||||
this._setValue(area.area_id);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.components.area-picker.failed_create_area"
|
||||
),
|
||||
text: err.message,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this._suggestion = undefined;
|
||||
this.comboBox.setInputValue("");
|
||||
}
|
||||
|
||||
private _setValue(value?: string) {
|
||||
this.value = value;
|
||||
setTimeout(() => {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-area-combo-box": HaAreaComboBox;
|
||||
}
|
||||
}
|
@@ -1,47 +1,24 @@
|
||||
import { mdiTextureBox } from "@mdi/js";
|
||||
import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import { mdiClose, mdiMenuDown, mdiShape, mdiTextureBox } from "@mdi/js";
|
||||
import type { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
|
||||
import type { HassEntity } from "home-assistant-js-websocket";
|
||||
import type { PropertyValues, TemplateResult } from "lit";
|
||||
import { LitElement, html } from "lit";
|
||||
import { LitElement, css, html, nothing, type CSSResultGroup } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { computeDomain } from "../common/entity/compute_domain";
|
||||
import type { ScorableTextItem } from "../common/string/filter/sequence-matching";
|
||||
import { fuzzyFilterSort } from "../common/string/filter/sequence-matching";
|
||||
import type { AreaRegistryEntry } from "../data/area_registry";
|
||||
import { createAreaRegistryEntry } from "../data/area_registry";
|
||||
import type {
|
||||
DeviceEntityDisplayLookup,
|
||||
DeviceRegistryEntry,
|
||||
} from "../data/device_registry";
|
||||
import { getDeviceEntityDisplayLookup } from "../data/device_registry";
|
||||
import type { EntityRegistryDisplayEntry } from "../data/entity_registry";
|
||||
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
||||
import { showAreaRegistryDetailDialog } from "../panels/config/areas/show-dialog-area-registry-detail";
|
||||
import type { HomeAssistant, ValueChangedEvent } from "../types";
|
||||
import { stopPropagation } from "../common/dom/stop_propagation";
|
||||
import { computeAreaName } from "../common/entity/compute_area_name";
|
||||
import { computeFloorName } from "../common/entity/compute_floor_name";
|
||||
import { getAreaContext } from "../common/entity/context/get_area_context";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import type { HomeAssistant } from "../types";
|
||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||
import "./ha-area-combo-box";
|
||||
import type { HaAreaComboBox } from "./ha-area-combo-box";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-combo-box-item";
|
||||
import type { HaComboBoxItem } from "./ha-combo-box-item";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-svg-icon";
|
||||
|
||||
type ScorableAreaRegistryEntry = ScorableTextItem & AreaRegistryEntry;
|
||||
|
||||
const rowRenderer: ComboBoxLitRenderer<AreaRegistryEntry> = (item) => html`
|
||||
<ha-combo-box-item type="button">
|
||||
${item.icon
|
||||
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
||||
: html`<ha-svg-icon slot="start" .path=${mdiTextureBox}></ha-svg-icon>`}
|
||||
${item.name}
|
||||
</ha-combo-box-item>
|
||||
`;
|
||||
|
||||
const ADD_NEW_ID = "___ADD_NEW___";
|
||||
const NO_ITEMS_ID = "___NO_ITEMS___";
|
||||
const ADD_NEW_SUGGESTION_ID = "___ADD_NEW_SUGGESTION___";
|
||||
|
||||
@customElement("ha-area-picker")
|
||||
export class HaAreaPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
@@ -99,389 +76,233 @@ export class HaAreaPicker extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public required = false;
|
||||
|
||||
@state() private _opened?: boolean;
|
||||
@property({ attribute: "hide-clear-icon", type: Boolean })
|
||||
public hideClearIcon = false;
|
||||
|
||||
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
||||
@query("#anchor") private _anchor?: HaComboBoxItem;
|
||||
|
||||
private _suggestion?: string;
|
||||
@query("#input") private _input?: HaAreaComboBox;
|
||||
|
||||
private _init = false;
|
||||
@state() private _opened = false;
|
||||
|
||||
public async open() {
|
||||
await this.updateComplete;
|
||||
await this.comboBox?.open();
|
||||
}
|
||||
private _renderContent() {
|
||||
const areaId = this.value || "";
|
||||
|
||||
public async focus() {
|
||||
await this.updateComplete;
|
||||
await this.comboBox?.focus();
|
||||
}
|
||||
|
||||
private _getAreas = memoizeOne(
|
||||
(
|
||||
areas: AreaRegistryEntry[],
|
||||
devices: DeviceRegistryEntry[],
|
||||
entities: EntityRegistryDisplayEntry[],
|
||||
includeDomains: this["includeDomains"],
|
||||
excludeDomains: this["excludeDomains"],
|
||||
includeDeviceClasses: this["includeDeviceClasses"],
|
||||
deviceFilter: this["deviceFilter"],
|
||||
entityFilter: this["entityFilter"],
|
||||
noAdd: this["noAdd"],
|
||||
excludeAreas: this["excludeAreas"]
|
||||
): AreaRegistryEntry[] => {
|
||||
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
|
||||
let inputDevices: DeviceRegistryEntry[] | undefined;
|
||||
let inputEntities: EntityRegistryDisplayEntry[] | undefined;
|
||||
|
||||
if (
|
||||
includeDomains ||
|
||||
excludeDomains ||
|
||||
includeDeviceClasses ||
|
||||
deviceFilter ||
|
||||
entityFilter
|
||||
) {
|
||||
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
|
||||
inputDevices = devices;
|
||||
inputEntities = entities.filter((entity) => entity.area_id);
|
||||
|
||||
if (includeDomains) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) =>
|
||||
includeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) =>
|
||||
includeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
}
|
||||
|
||||
if (excludeDomains) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return true;
|
||||
}
|
||||
return entities.every(
|
||||
(entity) =>
|
||||
!excludeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
});
|
||||
inputEntities = inputEntities!.filter(
|
||||
(entity) =>
|
||||
!excludeDomains.includes(computeDomain(entity.entity_id))
|
||||
);
|
||||
}
|
||||
|
||||
if (includeDeviceClasses) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||
);
|
||||
});
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
return (
|
||||
stateObj.attributes.device_class &&
|
||||
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (deviceFilter) {
|
||||
inputDevices = inputDevices!.filter((device) =>
|
||||
deviceFilter!(device)
|
||||
);
|
||||
}
|
||||
|
||||
if (entityFilter) {
|
||||
inputDevices = inputDevices!.filter((device) => {
|
||||
const devEntities = deviceEntityLookup[device.id];
|
||||
if (!devEntities || !devEntities.length) {
|
||||
return false;
|
||||
}
|
||||
return deviceEntityLookup[device.id].some((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return entityFilter(stateObj);
|
||||
});
|
||||
});
|
||||
inputEntities = inputEntities!.filter((entity) => {
|
||||
const stateObj = this.hass.states[entity.entity_id];
|
||||
if (!stateObj) {
|
||||
return false;
|
||||
}
|
||||
return entityFilter!(stateObj);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let outputAreas = areas;
|
||||
|
||||
let areaIds: string[] | undefined;
|
||||
|
||||
if (inputDevices) {
|
||||
areaIds = inputDevices
|
||||
.filter((device) => device.area_id)
|
||||
.map((device) => device.area_id!);
|
||||
}
|
||||
|
||||
if (inputEntities) {
|
||||
areaIds = (areaIds ?? []).concat(
|
||||
inputEntities
|
||||
.filter((entity) => entity.area_id)
|
||||
.map((entity) => entity.area_id!)
|
||||
);
|
||||
}
|
||||
|
||||
if (areaIds) {
|
||||
outputAreas = outputAreas.filter((area) =>
|
||||
areaIds!.includes(area.area_id)
|
||||
);
|
||||
}
|
||||
|
||||
if (excludeAreas) {
|
||||
outputAreas = outputAreas.filter(
|
||||
(area) => !excludeAreas!.includes(area.area_id)
|
||||
);
|
||||
}
|
||||
|
||||
if (!outputAreas.length) {
|
||||
outputAreas = [
|
||||
{
|
||||
area_id: NO_ITEMS_ID,
|
||||
floor_id: null,
|
||||
name: this.hass.localize("ui.components.area-picker.no_areas"),
|
||||
picture: null,
|
||||
icon: null,
|
||||
aliases: [],
|
||||
labels: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return noAdd
|
||||
? outputAreas
|
||||
: [
|
||||
...outputAreas,
|
||||
{
|
||||
area_id: ADD_NEW_ID,
|
||||
floor_id: null,
|
||||
name: this.hass.localize("ui.components.area-picker.add_new"),
|
||||
picture: null,
|
||||
icon: "mdi:plus",
|
||||
aliases: [],
|
||||
labels: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
];
|
||||
if (!areaId) {
|
||||
return html`
|
||||
<span slot="headline" class="placeholder"
|
||||
>${this.placeholder ??
|
||||
this.hass.localize("ui.components.area-picker.placeholder")}</span
|
||||
>
|
||||
<ha-svg-icon class="edit" slot="end" .path=${mdiMenuDown}></ha-svg-icon>
|
||||
`;
|
||||
}
|
||||
);
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
if (
|
||||
(!this._init && this.hass) ||
|
||||
(this._init && changedProps.has("_opened") && this._opened)
|
||||
) {
|
||||
this._init = true;
|
||||
const areas = this._getAreas(
|
||||
Object.values(this.hass.areas),
|
||||
Object.values(this.hass.devices),
|
||||
Object.values(this.hass.entities),
|
||||
this.includeDomains,
|
||||
this.excludeDomains,
|
||||
this.includeDeviceClasses,
|
||||
this.deviceFilter,
|
||||
this.entityFilter,
|
||||
this.noAdd,
|
||||
this.excludeAreas
|
||||
).map((area) => ({
|
||||
...area,
|
||||
strings: [area.area_id, ...area.aliases, area.name],
|
||||
}));
|
||||
this.comboBox.items = areas;
|
||||
this.comboBox.filteredItems = areas;
|
||||
const area = this.hass.areas[areaId];
|
||||
|
||||
const showClearIcon =
|
||||
!this.required && !this.disabled && !this.hideClearIcon;
|
||||
|
||||
if (!area) {
|
||||
return html`
|
||||
<ha-svg-icon slot="start" .path=${mdiShape}></ha-svg-icon>
|
||||
<span slot="headline">${area}</span>
|
||||
${showClearIcon
|
||||
? html`<ha-icon-button
|
||||
class="clear"
|
||||
slot="end"
|
||||
@click=${this._clear}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
<ha-svg-icon class="edit" slot="end" .path=${mdiMenuDown}></ha-svg-icon>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const { floor } = getAreaContext(area, this.hass);
|
||||
|
||||
const areaName = area ? computeAreaName(area) : undefined;
|
||||
const floorName = floor ? computeFloorName(floor) : undefined;
|
||||
|
||||
const icon = area.icon;
|
||||
|
||||
return html`
|
||||
<ha-combo-box
|
||||
.hass=${this.hass}
|
||||
.helper=${this.helper}
|
||||
item-value-path="area_id"
|
||||
item-id-path="area_id"
|
||||
item-label-path="name"
|
||||
.value=${this._value}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
.label=${this.label === undefined && this.hass
|
||||
? this.hass.localize("ui.components.area-picker.area")
|
||||
: this.label}
|
||||
.placeholder=${this.placeholder
|
||||
? this.hass.areas[this.placeholder]?.name
|
||||
: undefined}
|
||||
.renderer=${rowRenderer}
|
||||
@filter-changed=${this._filterChanged}
|
||||
@opened-changed=${this._openedChanged}
|
||||
@value-changed=${this._areaChanged}
|
||||
>
|
||||
</ha-combo-box>
|
||||
${icon
|
||||
? html`<ha-icon slot="start" .icon=${icon}></ha-icon>`
|
||||
: html`<ha-svg-icon slot="start" .path=${mdiTextureBox}></ha-svg-icon>`}
|
||||
<span slot="headline">${areaName}</span>
|
||||
${floorName
|
||||
? html`<span slot="supporting-text">${floorName}</span>`
|
||||
: nothing}
|
||||
${showClearIcon
|
||||
? html`<ha-icon-button
|
||||
class="clear"
|
||||
slot="end"
|
||||
@click=${this._clear}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>`
|
||||
: nothing}
|
||||
<ha-svg-icon class="edit" slot="end" .path=${mdiMenuDown}></ha-svg-icon>
|
||||
`;
|
||||
}
|
||||
|
||||
private _filterChanged(ev: CustomEvent): void {
|
||||
const target = ev.target as HaComboBox;
|
||||
const filterString = ev.detail.value;
|
||||
if (!filterString) {
|
||||
this.comboBox.filteredItems = this.comboBox.items;
|
||||
return;
|
||||
}
|
||||
|
||||
const filteredItems = fuzzyFilterSort<ScorableAreaRegistryEntry>(
|
||||
filterString,
|
||||
target.items?.filter(
|
||||
(item) => ![NO_ITEMS_ID, ADD_NEW_ID].includes(item.label_id)
|
||||
) || []
|
||||
);
|
||||
if (filteredItems.length === 0) {
|
||||
if (!this.noAdd) {
|
||||
this.comboBox.filteredItems = [
|
||||
{
|
||||
area_id: NO_ITEMS_ID,
|
||||
floor_id: null,
|
||||
name: this.hass.localize("ui.components.area-picker.no_match"),
|
||||
icon: null,
|
||||
picture: null,
|
||||
labels: [],
|
||||
aliases: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
] as AreaRegistryEntry[];
|
||||
} else {
|
||||
this._suggestion = filterString;
|
||||
this.comboBox.filteredItems = [
|
||||
{
|
||||
area_id: ADD_NEW_SUGGESTION_ID,
|
||||
floor_id: null,
|
||||
name: this.hass.localize(
|
||||
"ui.components.area-picker.add_new_sugestion",
|
||||
{ name: this._suggestion }
|
||||
),
|
||||
icon: "mdi:plus",
|
||||
picture: null,
|
||||
labels: [],
|
||||
aliases: [],
|
||||
temperature_entity_id: null,
|
||||
humidity_entity_id: null,
|
||||
created_at: 0,
|
||||
modified_at: 0,
|
||||
},
|
||||
] as AreaRegistryEntry[];
|
||||
}
|
||||
} else {
|
||||
this.comboBox.filteredItems = filteredItems;
|
||||
}
|
||||
protected render() {
|
||||
return html`
|
||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<div class="container">
|
||||
${!this._opened
|
||||
? html`<ha-combo-box-item
|
||||
.disabled=${this.disabled}
|
||||
id="anchor"
|
||||
type="button"
|
||||
compact
|
||||
@click=${this._showPicker}
|
||||
>
|
||||
${this._renderContent()}
|
||||
</ha-combo-box-item>`
|
||||
: html`<ha-area-combo-box
|
||||
id="input"
|
||||
.hass=${this.hass}
|
||||
.autofocus=${this.autofocus}
|
||||
.label=${this.hass.localize("ui.common.search")}
|
||||
.value=${this.value}
|
||||
.noAdd=${this.noAdd}
|
||||
.includeDomains=${this.includeDomains}
|
||||
.excludeDomains=${this.excludeDomains}
|
||||
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||
.entityFilter=${this.entityFilter}
|
||||
.excludeAreas=${this.excludeAreas}
|
||||
hide-clear-icon
|
||||
@opened-changed=${this._debounceOpenedChanged}
|
||||
@value-changed=${this._valueChanged}
|
||||
@input=${stopPropagation}
|
||||
></ha-area-combo-box>`}
|
||||
${this._renderHelper()}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private get _value() {
|
||||
return this.value || "";
|
||||
private _renderHelper() {
|
||||
return this.helper
|
||||
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`
|
||||
: nothing;
|
||||
}
|
||||
|
||||
private _openedChanged(ev: ValueChangedEvent<boolean>) {
|
||||
this._opened = ev.detail.value;
|
||||
private _clear(e) {
|
||||
e.stopPropagation();
|
||||
this.value = undefined;
|
||||
fireEvent(this, "value-changed", { value: undefined });
|
||||
fireEvent(this, "change");
|
||||
}
|
||||
|
||||
private _areaChanged(ev: ValueChangedEvent<string>) {
|
||||
ev.stopPropagation();
|
||||
let newValue = ev.detail.value;
|
||||
|
||||
if (newValue === NO_ITEMS_ID) {
|
||||
newValue = "";
|
||||
this.comboBox.setInputValue("");
|
||||
return;
|
||||
}
|
||||
|
||||
if (![ADD_NEW_SUGGESTION_ID, ADD_NEW_ID].includes(newValue)) {
|
||||
if (newValue !== this._value) {
|
||||
this._setValue(newValue);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
(ev.target as any).value = this._value;
|
||||
|
||||
this.hass.loadFragmentTranslation("config");
|
||||
|
||||
showAreaRegistryDetailDialog(this, {
|
||||
suggestedName: newValue === ADD_NEW_SUGGESTION_ID ? this._suggestion : "",
|
||||
createEntry: async (values) => {
|
||||
try {
|
||||
const area = await createAreaRegistryEntry(this.hass, values);
|
||||
const areas = [...Object.values(this.hass.areas), area];
|
||||
this.comboBox.filteredItems = this._getAreas(
|
||||
areas,
|
||||
Object.values(this.hass.devices)!,
|
||||
Object.values(this.hass.entities)!,
|
||||
this.includeDomains,
|
||||
this.excludeDomains,
|
||||
this.includeDeviceClasses,
|
||||
this.deviceFilter,
|
||||
this.entityFilter,
|
||||
this.noAdd,
|
||||
this.excludeAreas
|
||||
);
|
||||
await this.updateComplete;
|
||||
await this.comboBox.updateComplete;
|
||||
this._setValue(area.area_id);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.components.area-picker.failed_create_area"
|
||||
),
|
||||
text: err.message,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
this._suggestion = undefined;
|
||||
this.comboBox.setInputValue("");
|
||||
}
|
||||
|
||||
private _setValue(value?: string) {
|
||||
private _valueChanged(e) {
|
||||
e.stopPropagation();
|
||||
const value = e.detail.value;
|
||||
this.value = value;
|
||||
setTimeout(() => {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
fireEvent(this, "change");
|
||||
}, 0);
|
||||
fireEvent(this, "value-changed", { value });
|
||||
fireEvent(this, "change");
|
||||
}
|
||||
|
||||
private async _showPicker() {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
this._opened = true;
|
||||
await this.updateComplete;
|
||||
this._input?.focus();
|
||||
this._input?.open();
|
||||
}
|
||||
|
||||
// Multiple calls to _openedChanged can be triggered in quick succession
|
||||
// when the menu is opened
|
||||
private _debounceOpenedChanged = debounce(
|
||||
(ev) => this._openedChanged(ev),
|
||||
10
|
||||
);
|
||||
|
||||
private async _openedChanged(ev: ComboBoxLightOpenedChangedEvent) {
|
||||
const opened = ev.detail.value;
|
||||
if (this._opened && !opened) {
|
||||
this._opened = false;
|
||||
await this.updateComplete;
|
||||
this._anchor?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
css`
|
||||
mwc-menu-surface {
|
||||
--mdc-menu-min-width: 100%;
|
||||
}
|
||||
.container {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
ha-combo-box-item {
|
||||
background-color: var(--mdc-text-field-fill-color, whitesmoke);
|
||||
border-radius: 4px;
|
||||
border-end-end-radius: 0;
|
||||
border-end-start-radius: 0;
|
||||
--md-list-item-one-line-container-height: 56px;
|
||||
--md-list-item-two-line-container-height: 56px;
|
||||
--md-list-item-top-space: 8px;
|
||||
--md-list-item-bottom-space: 8px;
|
||||
--md-list-item-leading-space: 8px;
|
||||
--md-list-item-trailing-space: 8px;
|
||||
--ha-md-list-item-gap: 8px;
|
||||
/* Remove the default focus ring */
|
||||
--md-focus-ring-width: 0px;
|
||||
--md-focus-ring-duration: 0s;
|
||||
}
|
||||
|
||||
/* Add Similar focus style as the text field */
|
||||
ha-combo-box-item:after {
|
||||
display: block;
|
||||
content: "";
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: var(
|
||||
--mdc-text-field-idle-line-color,
|
||||
rgba(0, 0, 0, 0.42)
|
||||
);
|
||||
transform:
|
||||
height 180ms ease-in-out,
|
||||
background-color 180ms ease-in-out;
|
||||
}
|
||||
|
||||
ha-combo-box-item:focus:after {
|
||||
height: 2px;
|
||||
background-color: var(--mdc-theme-primary);
|
||||
}
|
||||
|
||||
ha-combo-box-item ha-svg-icon[slot="start"] {
|
||||
margin: 0 4px;
|
||||
}
|
||||
.clear {
|
||||
margin: 0 -8px;
|
||||
--mdc-icon-button-size: 32px;
|
||||
--mdc-icon-size: 20px;
|
||||
}
|
||||
.edit {
|
||||
--mdc-icon-size: 20px;
|
||||
width: 32px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.placeholder {
|
||||
color: var(--secondary-text-color);
|
||||
padding: 0 8px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -595,7 +595,7 @@ export class HaAssistChat extends LitElement {
|
||||
}
|
||||
.message {
|
||||
white-space: pre-line;
|
||||
font-size: 18px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
clear: both;
|
||||
margin: 8px 0;
|
||||
padding: 8px;
|
||||
@@ -604,7 +604,7 @@ export class HaAssistChat extends LitElement {
|
||||
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
.message {
|
||||
font-size: 16px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -95,9 +95,9 @@ export class HaBadge extends LitElement {
|
||||
text-align: center;
|
||||
}
|
||||
.label {
|
||||
font-size: 10px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
line-height: 10px;
|
||||
letter-spacing: 0.1px;
|
||||
color: var(--secondary-text-color);
|
||||
@@ -105,7 +105,7 @@ export class HaBadge extends LitElement {
|
||||
.content {
|
||||
font-size: var(--ha-badge-font-size, var(--ha-font-size-s));
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.1px;
|
||||
color: var(--primary-text-color);
|
||||
|
@@ -381,15 +381,18 @@ export class HaBaseTimeInput extends LitElement {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
label {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: var(--ha-moz-osx-font-smoothing);
|
||||
-webkit-font-smoothing: var(--ha-font-smoothing);
|
||||
font-family: var(
|
||||
--mdc-typography-body2-font-family,
|
||||
var(--mdc-typography-font-family, Roboto, sans-serif)
|
||||
var(--mdc-typography-font-family, var(--ha-font-family-body))
|
||||
);
|
||||
font-size: var(--mdc-typography-body2-font-size, 0.875rem);
|
||||
font-size: var(--mdc-typography-body2-font-size, var(--ha-font-size-s));
|
||||
line-height: var(--mdc-typography-body2-line-height, 1.25rem);
|
||||
font-weight: var(--mdc-typography-body2-font-weight, 400);
|
||||
font-weight: var(
|
||||
--mdc-typography-body2-font-weight,
|
||||
var(--ha-font-weight-normal)
|
||||
);
|
||||
letter-spacing: var(
|
||||
--mdc-typography-body2-letter-spacing,
|
||||
0.0178571429em
|
||||
|
@@ -41,14 +41,14 @@ export class HaCard extends LitElement {
|
||||
:host ::slotted(.card-header) {
|
||||
color: var(--ha-card-header-color, var(--primary-text-color));
|
||||
font-family: var(--ha-card-header-font-family, inherit);
|
||||
font-size: var(--ha-card-header-font-size, 24px);
|
||||
font-size: var(--ha-card-header-font-size, var(--ha-font-size-2xl));
|
||||
letter-spacing: -0.012em;
|
||||
line-height: 48px;
|
||||
padding: 12px 16px 16px;
|
||||
display: block;
|
||||
margin-block-start: 0px;
|
||||
margin-block-end: 0px;
|
||||
font-weight: normal;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
}
|
||||
|
||||
:host ::slotted(.card-content:not(:first-child)),
|
||||
|
@@ -154,7 +154,7 @@ class HaClimateState extends LitElement {
|
||||
}
|
||||
|
||||
.state-label {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
|
||||
.unit {
|
||||
|
@@ -22,12 +22,12 @@ export class HaComboBoxItem extends HaMdListItem {
|
||||
}
|
||||
[slot="headline"] {
|
||||
line-height: 22px;
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
white-space: nowrap;
|
||||
}
|
||||
[slot="supporting-text"] {
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
font-size: var(--ha-font-size-s);
|
||||
white-space: nowrap;
|
||||
}
|
||||
::slotted(state-badge),
|
||||
|
@@ -58,8 +58,8 @@ export class HaControlButton extends LitElement {
|
||||
padding: var(--control-button-padding);
|
||||
box-sizing: border-box;
|
||||
line-height: inherit;
|
||||
font-family: Roboto;
|
||||
font-weight: 500;
|
||||
font-family: var(--ha-font-family-body);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
background: none;
|
||||
|
@@ -194,7 +194,7 @@ export class HaControlNumberButton extends LitElement {
|
||||
color: var(--primary-text-color);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
transition: color 180ms ease-in-out;
|
||||
}
|
||||
:host([disabled]) {
|
||||
|
@@ -179,7 +179,7 @@ export class HaControlSelectMenu extends SelectBase {
|
||||
--control-select-menu-padding: 6px 10px;
|
||||
--mdc-icon-size: 20px;
|
||||
--ha-ripple-color: var(--secondary-text-color);
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
line-height: 1.4;
|
||||
width: auto;
|
||||
color: var(--primary-text-color);
|
||||
@@ -208,7 +208,7 @@ export class HaControlSelectMenu extends SelectBase {
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
letter-spacing: 0.25px;
|
||||
}
|
||||
.content {
|
||||
|
@@ -207,7 +207,7 @@ export class HaControlSelect extends LitElement {
|
||||
outline: none;
|
||||
transition: box-shadow 180ms ease-in-out;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
color: var(--primary-text-color);
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
|
@@ -368,7 +368,7 @@ export class HaControlSlider extends LitElement {
|
||||
--control-slider-background-opacity: 0.2;
|
||||
--control-slider-thickness: 40px;
|
||||
--control-slider-border-radius: 10px;
|
||||
--control-slider-tooltip-font-size: 14px;
|
||||
--control-slider-tooltip-font-size: var(--ha-font-size-m);
|
||||
height: var(--control-slider-thickness);
|
||||
width: 100%;
|
||||
border-radius: var(--control-slider-border-radius);
|
||||
|
@@ -53,12 +53,12 @@ export class HaDialogHeader extends LitElement {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.header-title {
|
||||
font-size: 22px;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
line-height: 28px;
|
||||
font-weight: 400;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
}
|
||||
.header-subtitle {
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
line-height: 20px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
@@ -85,12 +85,12 @@ export class HaDialog extends DialogBase {
|
||||
var(--dialog-backdrop-filter, none)
|
||||
);
|
||||
--mdc-dialog-box-shadow: var(--dialog-box-shadow, none);
|
||||
--mdc-typography-headline6-font-weight: 400;
|
||||
--mdc-typography-headline6-font-weight: var(--ha-font-weight-normal);
|
||||
--mdc-typography-headline6-font-size: 1.574rem;
|
||||
}
|
||||
.mdc-dialog__actions {
|
||||
justify-content: var(--justify-action-buttons, flex-end);
|
||||
padding-bottom: max(env(safe-area-inset-bottom), 24px);
|
||||
padding: 12px 24px max(env(safe-area-inset-bottom), 12px) 24px;
|
||||
}
|
||||
.mdc-dialog__actions span:nth-child(1) {
|
||||
flex: var(--secondary-action-button-flex, unset);
|
||||
@@ -107,9 +107,6 @@ export class HaDialog extends DialogBase {
|
||||
.mdc-dialog__title:has(span) {
|
||||
padding: 12px 12px 0;
|
||||
}
|
||||
.mdc-dialog__actions {
|
||||
padding: 12px 24px 12px 24px;
|
||||
}
|
||||
.mdc-dialog__title::before {
|
||||
content: unset;
|
||||
}
|
||||
|
@@ -188,7 +188,7 @@ export class HaExpansionPanel extends LitElement {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
outline: none;
|
||||
}
|
||||
#summary.noCollapse {
|
||||
@@ -218,7 +218,7 @@ export class HaExpansionPanel extends LitElement {
|
||||
.secondary {
|
||||
display: block;
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
font-size: var(--ha-font-size-s);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -294,7 +294,7 @@ export class HaFileUpload extends LitElement {
|
||||
}
|
||||
.supports {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
font-size: var(--ha-font-size-s);
|
||||
}
|
||||
:host([disabled]) .secondary {
|
||||
color: var(--disabled-text-color);
|
||||
@@ -324,7 +324,7 @@ export class HaFileUpload extends LitElement {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.header {
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
.progress {
|
||||
color: var(--secondary-text-color);
|
||||
@@ -333,7 +333,7 @@ export class HaFileUpload extends LitElement {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
color: var(--primary-color);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
|
@@ -208,8 +208,8 @@ export class HaFilterBlueprints extends LitElement {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -303,8 +303,8 @@ export class HaFilterCategories extends SubscribeMixin(LitElement) {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -232,8 +232,8 @@ export class HaFilterDevices extends LitElement {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -189,8 +189,8 @@ export class HaFilterDomains extends LitElement {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -246,8 +246,8 @@ export class HaFilterEntities extends LitElement {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -303,8 +303,8 @@ export class HaFilterFloorAreas extends LitElement {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -195,8 +195,8 @@ export class HaFilterIntegrations extends LitElement {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -233,8 +233,8 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -177,8 +177,8 @@ export class HaFilterStates extends LitElement {
|
||||
min-width: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
font-size: var(--ha-font-size-xs);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
background-color: var(--primary-color);
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
|
@@ -71,7 +71,10 @@ export class HaFormBoolean extends LitElement implements HaFormElement {
|
||||
box-sizing: border-box;
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 0.875rem;
|
||||
font-weight: var(--mdc-typography-body2-font-weight, 400);
|
||||
font-weight: var(
|
||||
--mdc-typography-body2-font-weight,
|
||||
var(--ha-font-weight-normal)
|
||||
);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ export class HaFormConstant extends LitElement implements HaFormElement {
|
||||
display: block;
|
||||
}
|
||||
.label {
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -46,7 +46,7 @@ export class HaHeaderBar extends LitElement {
|
||||
flex: none;
|
||||
}
|
||||
.mdc-top-app-bar__title {
|
||||
font-size: 20px;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
padding-inline-start: 24px;
|
||||
padding-inline-end: initial;
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ export class HaBadge extends LitElement {
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
color: var(--ha-heading-badge-text-color, var(--secondary-text-color));
|
||||
font-size: var(--ha-heading-badge-font-size, 14px);
|
||||
font-size: var(--ha-heading-badge-font-size, var(--ha-font-size-m));
|
||||
font-weight: var(--ha-heading-badge-font-weight, 400);
|
||||
line-height: var(--ha-heading-badge-line-height, 20px);
|
||||
letter-spacing: 0.1px;
|
||||
|
@@ -103,7 +103,7 @@ class HaHumidifierState extends LitElement {
|
||||
}
|
||||
|
||||
.state-label {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
|
||||
.unit {
|
||||
|
@@ -91,7 +91,7 @@ class HaLabelBadge extends LitElement {
|
||||
color: var(--ha-label-badge-label-color, white);
|
||||
border-radius: 1em;
|
||||
padding: 9% 16% 8% 16%; /* mostly apitalized text, not much descenders => bit more top margin */
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
overflow: hidden;
|
||||
text-transform: uppercase;
|
||||
text-overflow: ellipsis;
|
||||
@@ -102,7 +102,10 @@ class HaLabelBadge extends LitElement {
|
||||
margin-top: 1em;
|
||||
font-size: var(--ha-label-badge-title-font-size, 0.9em);
|
||||
width: var(--ha-label-badge-title-width, 5em);
|
||||
font-weight: var(--ha-label-badge-title-font-weight, 400);
|
||||
font-weight: var(
|
||||
--ha-label-badge-title-font-weight,
|
||||
var(--ha-font-weight-normal)
|
||||
);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
line-height: normal;
|
||||
|
@@ -32,8 +32,8 @@ class HaLabel extends LitElement {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-size: var(--ha-font-size-s);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.1px;
|
||||
vertical-align: middle;
|
||||
|
@@ -64,7 +64,7 @@ export class HaMarkdown extends LitElement {
|
||||
color: var(--markdown-svg-color, none);
|
||||
}
|
||||
code {
|
||||
font-size: 85%;
|
||||
font-size: var(--ha-font-size-s);
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
pre code {
|
||||
@@ -85,8 +85,8 @@ export class HaMarkdown extends LitElement {
|
||||
line-height: initial;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
hr {
|
||||
border-color: var(--divider-color);
|
||||
|
@@ -37,7 +37,7 @@ export class HaOutlinedTextField extends OutlinedTextField {
|
||||
--mdc-icon-size: var(--md-input-chip-icon-size, 18px);
|
||||
}
|
||||
.input {
|
||||
font-family: Roboto, sans-serif;
|
||||
font-family: var(--ha-font-family-body);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -154,8 +154,8 @@ export class HaSelectBox extends LitElement {
|
||||
}
|
||||
.option .content .text .label {
|
||||
color: var(--primary-text-color);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-size: var(--ha-font-size-m);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: 20px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
@@ -164,7 +164,7 @@ export class HaSelectBox extends LitElement {
|
||||
.option .content .text .description {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: 16px;
|
||||
}
|
||||
img {
|
||||
|
@@ -45,7 +45,7 @@ export class HaActionSelector extends LitElement {
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -62,7 +62,10 @@ export class HaBooleanSelector extends LitElement {
|
||||
box-sizing: border-box;
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 0.875rem;
|
||||
font-weight: var(--mdc-typography-body2-font-weight, 400);
|
||||
font-weight: var(
|
||||
--mdc-typography-body2-font-weight,
|
||||
var(--ha-font-weight-normal)
|
||||
);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -36,7 +36,7 @@ export class HaConditionSelector extends LitElement {
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import type { SchemaUnion } from "../ha-form/types";
|
||||
import type { MarkerLocation } from "../map/ha-locations-editor";
|
||||
import "../map/ha-locations-editor";
|
||||
import "../ha-form/ha-form";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
|
||||
@customElement("ha-selector-location")
|
||||
export class HaLocationSelector extends LitElement {
|
||||
@@ -27,7 +28,7 @@ export class HaLocationSelector extends LitElement {
|
||||
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(radius?: boolean, radius_readonly?: boolean) =>
|
||||
(localize: LocalizeFunc, radius?: boolean, radius_readonly?: boolean) =>
|
||||
[
|
||||
{
|
||||
name: "",
|
||||
@@ -36,12 +37,12 @@ export class HaLocationSelector extends LitElement {
|
||||
{
|
||||
name: "latitude",
|
||||
required: true,
|
||||
selector: { number: { step: "any" } },
|
||||
selector: { number: { step: "any", unit_of_measurement: "°" } },
|
||||
},
|
||||
{
|
||||
name: "longitude",
|
||||
required: true,
|
||||
selector: { number: { step: "any" } },
|
||||
selector: { number: { step: "any", unit_of_measurement: "°" } },
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -52,7 +53,16 @@ export class HaLocationSelector extends LitElement {
|
||||
required: true,
|
||||
default: 1000,
|
||||
disabled: !!radius_readonly,
|
||||
selector: { number: { min: 0, step: 1, mode: "box" } as const },
|
||||
selector: {
|
||||
number: {
|
||||
min: 0,
|
||||
step: 1,
|
||||
mode: "box",
|
||||
unit_of_measurement: localize(
|
||||
"ui.components.selectors.location.radius_meters"
|
||||
),
|
||||
} as const,
|
||||
},
|
||||
} as const,
|
||||
]
|
||||
: []),
|
||||
@@ -84,6 +94,7 @@ export class HaLocationSelector extends LitElement {
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.schema=${this._schema(
|
||||
this.hass.localize,
|
||||
this.selector.location?.radius,
|
||||
this.selector.location?.radius_readonly
|
||||
)}
|
||||
|
@@ -247,7 +247,7 @@ export class HaMediaSelector extends LitElement {
|
||||
--mdc-icon-size: calc(var(--media-browse-item-size, 175px) * 0.4);
|
||||
}
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
padding-top: 16px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@@ -290,7 +290,7 @@ export class HaSelectorSelector extends LitElement {
|
||||
padding: 0px 16px 16px 16px;
|
||||
}
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
padding-top: 16px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
@@ -45,7 +45,7 @@ export class HaTriggerSelector extends LitElement {
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -83,7 +83,7 @@ export class HaServiceControl extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public narrow = false;
|
||||
@property({ type: Boolean }) public narrow = false;
|
||||
|
||||
@property({ attribute: "show-advanced", type: Boolean }) public showAdvanced =
|
||||
false;
|
||||
@@ -895,6 +895,9 @@ export class HaServiceControl extends LitElement {
|
||||
ha-settings-row {
|
||||
padding: var(--service-control-padding, 0 16px);
|
||||
}
|
||||
ha-settings-row[narrow] {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
ha-settings-row {
|
||||
--settings-row-content-width: 100%;
|
||||
--settings-row-prefix-display: contents;
|
||||
@@ -916,7 +919,7 @@ export class HaServiceControl extends LitElement {
|
||||
margin: var(--service-control-padding, 0 16px);
|
||||
padding: 16px 0;
|
||||
}
|
||||
:host([hidePicker]) p {
|
||||
:host([hide-picker]) p {
|
||||
padding-top: 0;
|
||||
}
|
||||
.checkbox-spacer {
|
||||
|
@@ -66,11 +66,15 @@ export class HaSettingsRow extends LitElement {
|
||||
padding-top: 4px;
|
||||
font-family: var(
|
||||
--mdc-typography-body2-font-family,
|
||||
var(--mdc-typography-font-family, Roboto, sans-serif)
|
||||
var(--mdc-typography-font-family, var(--ha-font-family-body))
|
||||
);
|
||||
font-size: var(--mdc-typography-body2-font-size, var(--ha-font-size-s));
|
||||
-webkit-font-smoothing: var(--ha-font-smoothing);
|
||||
-moz-osx-font-smoothing: var(--ha-moz-osx-font-smoothing);
|
||||
font-weight: var(
|
||||
--mdc-typography-body2-font-weight,
|
||||
var(--ha-font-weight-normal)
|
||||
);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: var(--mdc-typography-body2-font-size, 0.875rem);
|
||||
font-weight: var(--mdc-typography-body2-font-weight, 400);
|
||||
line-height: normal;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
@@ -24,9 +24,10 @@ import {
|
||||
customElement,
|
||||
eventOptions,
|
||||
property,
|
||||
state,
|
||||
query,
|
||||
state,
|
||||
} from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { storage } from "../common/decorators/storage";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
@@ -45,13 +46,13 @@ import { haStyleScrollbar } from "../resources/styles";
|
||||
import type { HomeAssistant, PanelInfo, Route } from "../types";
|
||||
import "./ha-icon";
|
||||
import "./ha-icon-button";
|
||||
import "./ha-md-list";
|
||||
import "./ha-md-list-item";
|
||||
import type { HaMdListItem } from "./ha-md-list-item";
|
||||
import "./ha-menu-button";
|
||||
import "./ha-sortable";
|
||||
import "./ha-svg-icon";
|
||||
import "./user/ha-user-badge";
|
||||
import "./ha-md-list";
|
||||
import "./ha-md-list-item";
|
||||
import type { HaMdListItem } from "./ha-md-list-item";
|
||||
|
||||
const SHOW_AFTER_SPACER = ["config", "developer-tools"];
|
||||
|
||||
@@ -407,6 +408,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
|
||||
// prettier-ignore
|
||||
return html`
|
||||
<ha-sortable .disabled=${!this.editMode} draggable-selector=".draggable" @item-moved=${this._panelMoved}>
|
||||
<ha-md-list
|
||||
class="ha-scrollbar"
|
||||
@focusin=${this._listboxFocusIn}
|
||||
@@ -420,11 +422,16 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
${this._renderSpacer()}
|
||||
${this._renderPanels(afterSpacer, selectedPanel)}
|
||||
${this._renderExternalConfiguration()}
|
||||
</ha-md-list>
|
||||
</ha-md-list>
|
||||
</ha-sortable>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderPanels(panels: PanelInfo[], selectedPanel: string) {
|
||||
private _renderPanels(
|
||||
panels: PanelInfo[],
|
||||
selectedPanel: string,
|
||||
sortable = false
|
||||
) {
|
||||
return panels.map((panel) =>
|
||||
this._renderPanel(
|
||||
panel.url_path,
|
||||
@@ -437,17 +444,26 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
: panel.url_path in PANEL_ICONS
|
||||
? PANEL_ICONS[panel.url_path]
|
||||
: undefined,
|
||||
selectedPanel
|
||||
selectedPanel,
|
||||
sortable
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _renderPanelsEdit(beforeSpacer: PanelInfo[], selectedPanel: string) {
|
||||
return html`
|
||||
${this._renderPanels(beforeSpacer, selectedPanel, true)}
|
||||
${this._renderSpacer()}${this._renderHiddenPanels()}
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderPanel(
|
||||
urlPath: string,
|
||||
title: string | null,
|
||||
icon: string | null | undefined,
|
||||
iconPath: string | null | undefined,
|
||||
selectedPanel: string
|
||||
selectedPanel: string,
|
||||
sortable = false
|
||||
) {
|
||||
return urlPath === "config"
|
||||
? this._renderConfiguration(title, selectedPanel)
|
||||
@@ -455,7 +471,10 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
<ha-md-list-item
|
||||
.href=${this.editMode ? undefined : `/${urlPath}`}
|
||||
type="link"
|
||||
class=${selectedPanel === urlPath ? "selected" : ""}
|
||||
class=${classMap({
|
||||
selected: selectedPanel === urlPath,
|
||||
draggable: this.editMode && sortable,
|
||||
})}
|
||||
@mouseenter=${this._itemMouseEnter}
|
||||
@mouseleave=${this._itemMouseLeave}
|
||||
>
|
||||
@@ -496,15 +515,6 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
this._panelOrder = panelOrder;
|
||||
}
|
||||
|
||||
private _renderPanelsEdit(beforeSpacer: PanelInfo[], selectedPanel: string) {
|
||||
return html`
|
||||
<ha-sortable .disabled=${!this.editMode} @item-moved=${this._panelMoved}
|
||||
><div>${this._renderPanels(beforeSpacer, selectedPanel)}</div>
|
||||
</ha-sortable>
|
||||
${this._renderSpacer()}${this._renderHiddenPanels()}
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderHiddenPanels() {
|
||||
return html`${this._hiddenPanels.length
|
||||
? html`${this._hiddenPanels.map((url) => {
|
||||
@@ -829,7 +839,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
padding: 0 4px;
|
||||
border-bottom: 1px solid transparent;
|
||||
white-space: nowrap;
|
||||
font-weight: 400;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
color: var(
|
||||
--sidebar-menu-button-text-color,
|
||||
var(--primary-text-color)
|
||||
@@ -839,7 +849,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
--sidebar-menu-button-background-color,
|
||||
inherit
|
||||
);
|
||||
font-size: 20px;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
align-items: center;
|
||||
padding-left: calc(4px + env(safe-area-inset-left));
|
||||
padding-inline-start: calc(4px + env(safe-area-inset-left));
|
||||
@@ -939,9 +949,10 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
|
||||
ha-md-list-item .item-text {
|
||||
font-family: var(--ha-font-family-body);
|
||||
display: none;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
:host([expanded]) ha-md-list-item .item-text {
|
||||
display: block;
|
||||
@@ -963,7 +974,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
align-items: center;
|
||||
min-width: 8px;
|
||||
border-radius: 10px;
|
||||
font-weight: 400;
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: normal;
|
||||
background-color: var(--accent-color);
|
||||
padding: 2px 6px;
|
||||
@@ -997,8 +1008,8 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
|
||||
.subheader {
|
||||
color: var(--sidebar-text-color);
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
padding: 16px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -1012,7 +1023,7 @@ class HaSidebar extends SubscribeMixin(LitElement) {
|
||||
color: var(--sidebar-background-color);
|
||||
background-color: var(--sidebar-text-color);
|
||||
padding: 4px;
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
|
||||
.menu ha-icon-button {
|
||||
|
@@ -38,7 +38,7 @@ class HaTip extends LitElement {
|
||||
}
|
||||
|
||||
.prefix {
|
||||
font-weight: 500;
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ export class HaTopAppBarFixed extends TopAppBarFixedBase {
|
||||
padding-top: var(--header-height);
|
||||
}
|
||||
.mdc-top-app-bar {
|
||||
--mdc-typography-headline6-font-weight: 400;
|
||||
--mdc-typography-headline6-font-weight: var(--ha-font-weight-normal);
|
||||
color: var(--app-header-text-color, var(--mdc-theme-on-primary, #fff));
|
||||
background-color: var(
|
||||
--app-header-background-color,
|
||||
@@ -24,7 +24,7 @@ export class HaTopAppBarFixed extends TopAppBarFixedBase {
|
||||
);
|
||||
}
|
||||
.mdc-top-app-bar__title {
|
||||
font-size: 20px;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
padding-inline-start: 24px;
|
||||
padding-inline-end: initial;
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ export class HaTopAppBar extends TopAppBarBase {
|
||||
padding-top: var(--header-height);
|
||||
}
|
||||
.mdc-top-app-bar {
|
||||
--mdc-typography-headline6-font-weight: 400;
|
||||
--mdc-typography-headline6-font-weight: var(--ha-font-weight-normal);
|
||||
color: var(--app-header-text-color, var(--mdc-theme-on-primary, #fff));
|
||||
background-color: var(
|
||||
--app-header-background-color,
|
||||
|
@@ -268,7 +268,7 @@ export class TopAppBarBaseBase extends BaseElement {
|
||||
);
|
||||
}
|
||||
.mdc-top-app-bar {
|
||||
--mdc-typography-headline6-font-weight: 400;
|
||||
--mdc-typography-headline6-font-weight: var(--ha-font-weight-normal);
|
||||
color: var(--app-header-text-color, var(--mdc-theme-on-primary, #fff));
|
||||
background-color: var(
|
||||
--app-header-background-color,
|
||||
@@ -321,7 +321,7 @@ export class TopAppBarBaseBase extends BaseElement {
|
||||
overflow: auto;
|
||||
}
|
||||
.mdc-top-app-bar__title {
|
||||
font-size: 20px;
|
||||
font-size: var(--ha-font-size-xl);
|
||||
padding-inline-start: 24px;
|
||||
padding-inline-end: initial;
|
||||
}
|
||||
|
@@ -71,7 +71,7 @@ export class HaWaterHeaterState extends LitElement {
|
||||
}
|
||||
|
||||
.state-label {
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
}
|
||||
|
||||
.label {
|
||||
|
@@ -58,7 +58,7 @@ class HaEntityMarker extends LitElement {
|
||||
box-sizing: border-box;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
font-size: var(--ha-marker-font-size, 1.5em);
|
||||
font-size: var(--ha-marker-font-size, var(--ha-font-size-xl));
|
||||
border-radius: var(--ha-marker-border-radius, 50%);
|
||||
border: 1px solid var(--ha-marker-color, var(--primary-color));
|
||||
color: var(--primary-text-color);
|
||||
|
@@ -671,7 +671,7 @@ export class HaMap extends ReactiveElement {
|
||||
}
|
||||
.leaflet-tooltip {
|
||||
padding: 8px;
|
||||
font-size: 90%;
|
||||
font-size: var(--ha-font-size-s);
|
||||
background: rgba(80, 80, 80, 0.9) !important;
|
||||
color: white !important;
|
||||
border-radius: 4px;
|
||||
@@ -688,7 +688,7 @@ export class HaMap extends ReactiveElement {
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
color: var(--text-primary-color);
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
}
|
||||
|
||||
.marker-cluster span {
|
||||
|
@@ -965,9 +965,9 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
padding-top: 16px;
|
||||
}
|
||||
.breadcrumb .title {
|
||||
font-size: 32px;
|
||||
font-size: var(--ha-font-size-4xl);
|
||||
line-height: 1.2;
|
||||
font-weight: bold;
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
@@ -976,7 +976,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
padding-right: 8px;
|
||||
}
|
||||
.breadcrumb .previous-title {
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
padding-bottom: 8px;
|
||||
color: var(--secondary-text-color);
|
||||
overflow: hidden;
|
||||
@@ -985,7 +985,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
--mdc-icon-size: 14px;
|
||||
}
|
||||
.breadcrumb .subtitle {
|
||||
font-size: 16px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom: 0;
|
||||
@@ -1144,7 +1144,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
}
|
||||
|
||||
.child .title {
|
||||
font-size: 16px;
|
||||
font-size: var(--ha-font-size-l);
|
||||
padding-top: 16px;
|
||||
padding-left: 2px;
|
||||
overflow: hidden;
|
||||
@@ -1209,7 +1209,7 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
}
|
||||
|
||||
:host([narrow]) .breadcrumb .title {
|
||||
font-size: 24px;
|
||||
font-size: var(--ha-font-size-2xl);
|
||||
}
|
||||
:host([narrow]) .header {
|
||||
padding: 0;
|
||||
|
@@ -34,15 +34,15 @@ export class HaTileInfo extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
.primary {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
font-size: var(--ha-font-size-m);
|
||||
font-weight: var(--ha-font-weight-medium);
|
||||
line-height: 20px;
|
||||
letter-spacing: 0.1px;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.secondary {
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
font-size: var(--ha-font-size-s);
|
||||
font-weight: var(--ha-font-weight-normal);
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.4px;
|
||||
color: var(--primary-text-color);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user