mirror of
https://github.com/home-assistant/frontend.git
synced 2026-06-30 04:02:17 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c080ebbf46 |
+541
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+874
File diff suppressed because one or more lines are too long
Vendored
-893
File diff suppressed because one or more lines are too long
+7
-5
@@ -1,9 +1,11 @@
|
||||
compressionLevel: mixed
|
||||
|
||||
defaultSemverRangePrefix: ""
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.0.1.cjs
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
|
||||
spec: "@yarnpkg/plugin-typescript"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.4.cjs
|
||||
|
||||
@@ -12,7 +12,11 @@ module.exports.sourceMapURL = () => {
|
||||
};
|
||||
|
||||
// Files from NPM Packages that should not be imported
|
||||
module.exports.ignorePackages = () => [];
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
module.exports.ignorePackages = ({ latestBuild }) => [
|
||||
// Part of yaml.js and only used for !!js functions that we don't use
|
||||
require.resolve("esprima"),
|
||||
];
|
||||
|
||||
// Files from NPM packages that we should replace with empty file
|
||||
module.exports.emptyPackages = ({ latestBuild, isHassioBuild }) =>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { mdiCast, mdiCastConnected } from "@mdi/js";
|
||||
import "@polymer/paper-item/paper-icon-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import { Auth, Connection } from "home-assistant-js-websocket";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { CastManager } from "../../../../src/cast/cast_manager";
|
||||
import {
|
||||
@@ -22,9 +22,8 @@ import "../../../../src/components/ha-svg-icon";
|
||||
import {
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types";
|
||||
import { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
|
||||
import "../../../../src/layouts/hass-loading-screen";
|
||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
|
||||
import "./hc-layout";
|
||||
@@ -39,10 +38,10 @@ class HcCast extends LitElement {
|
||||
|
||||
@state() private askWrite = false;
|
||||
|
||||
@state() private lovelaceViews?: LovelaceViewConfig[] | null;
|
||||
@state() private lovelaceConfig?: LovelaceConfig | null;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (this.lovelaceViews === undefined) {
|
||||
if (this.lovelaceConfig === undefined) {
|
||||
return html`<hass-loading-screen no-toolbar></hass-loading-screen>`;
|
||||
}
|
||||
|
||||
@@ -87,10 +86,9 @@ class HcCast extends LitElement {
|
||||
attr-for-selected="data-path"
|
||||
.selected=${this.castManager.status.lovelacePath || ""}
|
||||
>
|
||||
${(
|
||||
this.lovelaceViews ?? [
|
||||
generateDefaultViewConfig({}, {}, {}, {}, () => ""),
|
||||
]
|
||||
${(this.lovelaceConfig
|
||||
? this.lovelaceConfig.views
|
||||
: [generateDefaultViewConfig({}, {}, {}, {}, () => "")]
|
||||
).map(
|
||||
(view, idx) => html`
|
||||
<paper-icon-item
|
||||
@@ -138,15 +136,11 @@ class HcCast extends LitElement {
|
||||
llColl.refresh().then(
|
||||
() => {
|
||||
llColl.subscribe((config) => {
|
||||
if (isStrategyDashboard(config)) {
|
||||
this.lovelaceViews = null;
|
||||
} else {
|
||||
this.lovelaceViews = config.views;
|
||||
}
|
||||
this.lovelaceConfig = config;
|
||||
});
|
||||
},
|
||||
async () => {
|
||||
this.lovelaceViews = null;
|
||||
this.lovelaceConfig = null;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -165,7 +159,9 @@ class HcCast extends LitElement {
|
||||
toggleAttribute(
|
||||
this,
|
||||
"hide-icons",
|
||||
this.lovelaceViews ? !this.lovelaceViews.some((view) => view.icon) : true
|
||||
this.lovelaceConfig
|
||||
? !this.lovelaceConfig.views.some((view) => view.icon)
|
||||
: true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { LovelaceCardConfig } from "../../../../src/data/lovelace/config/card";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import {
|
||||
LovelaceCardConfig,
|
||||
LovelaceConfig,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { castContext } from "../cast_context";
|
||||
|
||||
export const castDemoLovelace: () => LovelaceConfig = () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { mockHistory } from "../../../../demo/src/stubs/history";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import {
|
||||
MockHomeAssistant,
|
||||
provideHass,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
|
||||
import { LovelaceConfig } from "../../../../src/data/lovelace";
|
||||
import { Lovelace } from "../../../../src/panels/lovelace/types";
|
||||
import "../../../../src/panels/lovelace/views/hui-view";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
@@ -14,8 +14,7 @@ import "./hc-launch-screen";
|
||||
class HcLovelace extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ attribute: false })
|
||||
public lovelaceConfig!: LovelaceConfig;
|
||||
@property({ attribute: false }) public lovelaceConfig!: LovelaceConfig;
|
||||
|
||||
@property() public viewPath?: string | number;
|
||||
|
||||
|
||||
@@ -21,26 +21,18 @@ import {
|
||||
import { atLeastVersion } from "../../../../src/common/config/version";
|
||||
import { isNavigationClick } from "../../../../src/common/dom/is-navigation-click";
|
||||
import {
|
||||
fetchResources,
|
||||
getLegacyLovelaceCollection,
|
||||
getLovelaceCollection,
|
||||
} from "../../../../src/data/lovelace";
|
||||
import {
|
||||
isStrategyDashboard,
|
||||
LegacyLovelaceConfig,
|
||||
LovelaceConfig,
|
||||
LovelaceDashboardStrategyConfig,
|
||||
} from "../../../../src/data/lovelace/config/types";
|
||||
import { fetchResources } from "../../../../src/data/lovelace/resource";
|
||||
} from "../../../../src/data/lovelace";
|
||||
import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/load-resources";
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import { castContext } from "../cast_context";
|
||||
import "./hc-launch-screen";
|
||||
|
||||
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
|
||||
strategy: {
|
||||
type: "original-states",
|
||||
},
|
||||
};
|
||||
const DEFAULT_STRATEGY = "original-states";
|
||||
|
||||
let resourcesLoaded = false;
|
||||
@customElement("hc-main")
|
||||
@@ -101,7 +93,7 @@ export class HcMain extends HassElement {
|
||||
.lovelaceConfig=${this._lovelaceConfig}
|
||||
.viewPath=${this._lovelacePath}
|
||||
.urlPath=${this._urlPath}
|
||||
@config-refresh=${this._generateDefaultLovelaceConfig}
|
||||
@config-refresh=${this._generateLovelaceConfig}
|
||||
></hc-lovelace>
|
||||
`;
|
||||
}
|
||||
@@ -292,20 +284,9 @@ export class HcMain extends HassElement {
|
||||
// configuration.
|
||||
try {
|
||||
await llColl.refresh();
|
||||
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
|
||||
if (isStrategyDashboard(rawConfig)) {
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
const config = await generateLovelaceDashboardStrategy(
|
||||
rawConfig.strategy,
|
||||
this.hass!
|
||||
);
|
||||
this._handleNewLovelaceConfig(config);
|
||||
} else {
|
||||
this._handleNewLovelaceConfig(rawConfig);
|
||||
}
|
||||
});
|
||||
this._unsubLovelace = llColl.subscribe((lovelaceConfig) =>
|
||||
this._handleNewLovelaceConfig(lovelaceConfig)
|
||||
);
|
||||
} catch (err: any) {
|
||||
if (
|
||||
atLeastVersion(this.hass.connection.haVersion, 0, 107) &&
|
||||
@@ -319,7 +300,7 @@ export class HcMain extends HassElement {
|
||||
}
|
||||
// Generate a Lovelace config.
|
||||
this._unsubLovelace = () => undefined;
|
||||
await this._generateDefaultLovelaceConfig();
|
||||
await this._generateLovelaceConfig();
|
||||
}
|
||||
}
|
||||
if (!resourcesLoaded) {
|
||||
@@ -335,13 +316,15 @@ export class HcMain extends HassElement {
|
||||
this._sendStatus();
|
||||
}
|
||||
|
||||
private async _generateDefaultLovelaceConfig() {
|
||||
private async _generateLovelaceConfig() {
|
||||
const { generateLovelaceDashboardStrategy } = await import(
|
||||
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||
);
|
||||
this._handleNewLovelaceConfig(
|
||||
await generateLovelaceDashboardStrategy(
|
||||
DEFAULT_CONFIG.strategy,
|
||||
{
|
||||
type: DEFAULT_STRATEGY,
|
||||
},
|
||||
this.hass!
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LocalizeFunc } from "../../../src/common/translations/localize";
|
||||
import { LovelaceConfig } from "../../../src/data/lovelace/config/types";
|
||||
import { LovelaceConfig } from "../../../src/data/lovelace";
|
||||
import { Entity } from "../../../src/fake_data/entity";
|
||||
|
||||
export interface DemoConfig {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { customElement, property, state } from "lit/decorators";
|
||||
import { until } from "lit/directives/until";
|
||||
import "../../../src/components/ha-card";
|
||||
import "../../../src/components/ha-circular-progress";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace/config/card";
|
||||
import { LovelaceCardConfig } from "../../../src/data/lovelace";
|
||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||
import { Lovelace, LovelaceCard } from "../../../src/panels/lovelace/types";
|
||||
import {
|
||||
|
||||
@@ -74,9 +74,19 @@
|
||||
<body>
|
||||
<div id="ha-launch-screen">
|
||||
<div class="ha-launch-screen-spacer"></div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
|
||||
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
|
||||
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
|
||||
<svg
|
||||
viewBox="0 0 240 240"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
|
||||
fill="#F2F4F9"
|
||||
/>
|
||||
<path
|
||||
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
|
||||
fill="#18BCF2"
|
||||
/>
|
||||
</svg>
|
||||
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { css, html, LitElement, TemplateResult, nothing } from "lit";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/chips/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-assist-chip";
|
||||
import "../../../../src/components/chips/ha-input-chip";
|
||||
import "../../../../src/components/chips/ha-filter-chip";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import "../../../../src/components/ha-chip-set";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg";
|
||||
|
||||
@@ -12,6 +10,10 @@ const chips: {
|
||||
icon?: string;
|
||||
content?: string;
|
||||
}[] = [
|
||||
{},
|
||||
{
|
||||
icon: mdiHomeAssistant,
|
||||
},
|
||||
{
|
||||
content: "Content",
|
||||
},
|
||||
@@ -27,73 +29,31 @@ export class DemoHaChips extends LitElement {
|
||||
return html`
|
||||
<ha-card header="ha-chip demo">
|
||||
<div class="card-content">
|
||||
<p>Action chip</p>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: ""}
|
||||
${chip.content}
|
||||
</ha-chip>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</ha-card>
|
||||
<ha-card header="ha-chip-set demo">
|
||||
<div class="card-content">
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-assist-chip .label=${chip.content}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-assist-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-assist-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-assist-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
<p>Filter chip</p>
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-filter-chip .label=${chip.content}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-filter-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-filter-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-filter-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
<p>Input chip</p>
|
||||
<ha-chip-set>
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-input-chip .label=${chip.content}>
|
||||
<ha-chip .hasIcon=${chip.icon !== undefined}>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: ""}
|
||||
${chip.content}
|
||||
</ha-input-chip>
|
||||
`
|
||||
)}
|
||||
${chips.map(
|
||||
(chip) => html`
|
||||
<ha-input-chip .label=${chip.content} selected>
|
||||
${chip.icon
|
||||
? html`<ha-svg-icon slot="icon" .path=${chip.icon}>
|
||||
</ha-svg-icon>`
|
||||
: nothing}
|
||||
</ha-input-chip>
|
||||
</ha-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
@@ -108,10 +68,12 @@ export class DemoHaChips extends LitElement {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
ha-chip {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ const buttons: {
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
unit?: string;
|
||||
class?: string;
|
||||
}[] = [
|
||||
{
|
||||
@@ -30,11 +29,6 @@ const buttons: {
|
||||
label: "Custom",
|
||||
class: "custom",
|
||||
},
|
||||
{
|
||||
id: "unit",
|
||||
label: "With unit",
|
||||
unit: "m",
|
||||
},
|
||||
];
|
||||
|
||||
@customElement("demo-components-ha-control-number-buttons")
|
||||
@@ -56,7 +50,6 @@ export class DemoHarControlNumberButtons extends LitElement {
|
||||
<pre>Config: ${JSON.stringify(config)}</pre>
|
||||
<ha-control-number-buttons
|
||||
.value=${this.value}
|
||||
.unit=${config.unit}
|
||||
.min=${config.min}
|
||||
.max=${config.max}
|
||||
.step=${config.step}
|
||||
|
||||
@@ -9,7 +9,6 @@ const sliders: {
|
||||
id: string;
|
||||
label: string;
|
||||
mode?: "start" | "end" | "cursor";
|
||||
unit?: string;
|
||||
class?: string;
|
||||
}[] = [
|
||||
{
|
||||
@@ -32,21 +31,18 @@ const sliders: {
|
||||
label: "Slider (start mode) and custom style",
|
||||
mode: "start",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
{
|
||||
id: "slider-end-custom",
|
||||
label: "Slider (end mode) and custom style",
|
||||
mode: "end",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
{
|
||||
id: "slider-cursor-custom",
|
||||
label: "Slider (cursor mode) and custom style",
|
||||
mode: "cursor",
|
||||
class: "custom",
|
||||
unit: "mm",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -97,7 +93,6 @@ export class DemoHaBarSlider extends LitElement {
|
||||
@value-changed=${this.handleValueChanged}
|
||||
@slider-moved=${this.handleSliderMoved}
|
||||
aria-labelledby=${id}
|
||||
.unit=${config.unit}
|
||||
>
|
||||
</ha-control-slider>
|
||||
</div>
|
||||
@@ -119,7 +114,6 @@ export class DemoHaBarSlider extends LitElement {
|
||||
@value-changed=${this.handleValueChanged}
|
||||
@slider-moved=${this.handleSliderMoved}
|
||||
aria-label=${label}
|
||||
.unit=${config.unit}
|
||||
>
|
||||
</ha-control-slider>
|
||||
`;
|
||||
|
||||
@@ -5,22 +5,9 @@ subtitle: Dialogs provide important prompts in a user flow.
|
||||
|
||||
# Material Design 3
|
||||
|
||||
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guideliness. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
||||
Our dialogs are based on the latest version of Material Design. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
|
||||
|
||||
# Guidelines
|
||||
|
||||
## Design
|
||||
|
||||
- Dialogs have a max width of 560px. Alert and confirmation dialogs got a fixed width of 320px. If you need more width, consider a dedicated page instead.
|
||||
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guideliness.
|
||||
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
|
||||
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
|
||||
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
|
||||
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
||||
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
||||
- Destructive actions should be a red warning button.
|
||||
- Alert or confirmation dialogs only have buttons and no X-icon.
|
||||
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
||||
# Highlighted guidelines
|
||||
|
||||
## Content
|
||||
|
||||
@@ -30,6 +17,14 @@ Our dialogs are based on the latest version of Material Design. Please note that
|
||||
- If users become unsure, they read the description. Make sure this explains what will happen.
|
||||
- Strive for minimalism.
|
||||
|
||||
## Buttons and X-icon
|
||||
|
||||
- Keep the labels short, for example `Save`, `Delete`, `Enable`.
|
||||
- Dialog with actions must always have a discard button. On desktop a `Cancel` button and X-icon, on mobile only the X-icon.
|
||||
- Destructive actions should be a red warning button.
|
||||
- Alert or confirmation dialogs only have buttons and no X-icon.
|
||||
- Try to avoid three buttons in one dialog. Especially when you leave the dialog task unfinished.
|
||||
|
||||
## Example
|
||||
|
||||
### Confirmation dialog
|
||||
|
||||
@@ -10,6 +10,7 @@ import { computeStateDisplay } from "../../../../src/common/entity/compute_state
|
||||
import "../../../../src/components/data-table/ha-data-table";
|
||||
import type { DataTableColumnContainer } from "../../../../src/components/data-table/ha-data-table";
|
||||
import "../../../../src/components/entity/state-badge";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import "@material/mwc-button";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import "../../../../src/components/ha-card";
|
||||
import { ActionHandlerEvent } from "../../../../src/data/lovelace/action_handler";
|
||||
import { ActionHandlerEvent } from "../../../../src/data/lovelace";
|
||||
import { actionHandler } from "../../../../src/panels/lovelace/common/directives/action-handler-directive";
|
||||
|
||||
@customElement("demo-misc-util-long-press")
|
||||
|
||||
@@ -31,8 +31,8 @@ import { navigate } from "../../../../src/common/navigate";
|
||||
import "../../../../src/components/buttons/ha-progress-button";
|
||||
import "../../../../src/components/ha-alert";
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/chips/ha-chip-set";
|
||||
import "../../../../src/components/chips/ha-assist-chip";
|
||||
import "../../../../src/components/ha-chip";
|
||||
import "../../../../src/components/ha-chip-set";
|
||||
import "../../../../src/components/ha-markdown";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import "../../../../src/components/ha-svg-icon";
|
||||
@@ -78,7 +78,6 @@ import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-has
|
||||
import { hassioStyle } from "../../resources/hassio-style";
|
||||
import "../../update-available/update-available-card";
|
||||
import { addonArchIsSupported, extractChangelog } from "../../util/addon";
|
||||
import { capitalizeFirstLetter } from "../../../../src/common/string/capitalize-first-letter";
|
||||
|
||||
const STAGE_ICON = {
|
||||
stable: mdiCheckCircle,
|
||||
@@ -235,32 +234,28 @@ class HassioAddonInfo extends LitElement {
|
||||
|
||||
<ha-chip-set class="capabilities">
|
||||
${this.addon.stage !== "stable"
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
||||
)
|
||||
)}
|
||||
? html` <ha-chip
|
||||
hasIcon
|
||||
class=${classMap({
|
||||
yellow: this.addon.stage === "experimental",
|
||||
red: this.addon.stage === "deprecated",
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="stage"
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${STAGE_ICON[this.addon.stage]}
|
||||
>
|
||||
</ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
`
|
||||
</ha-svg-icon>
|
||||
${this.supervisor.localize(
|
||||
`addon.dashboard.capability.stages.${this.addon.stage}`
|
||||
)}
|
||||
</ha-chip>`
|
||||
: ""}
|
||||
|
||||
<ha-assist-chip
|
||||
filled
|
||||
<ha-chip
|
||||
hasIcon
|
||||
class=${classMap({
|
||||
green: Number(this.addon.rating) >= 6,
|
||||
yellow: [3, 4, 5].includes(Number(this.addon.rating)),
|
||||
@@ -268,183 +263,138 @@ class HassioAddonInfo extends LitElement {
|
||||
})}
|
||||
@click=${this._showMoreInfo}
|
||||
id="rating"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.rating"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${RATING_ICON[this.addon.rating]}>
|
||||
</ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.rating"
|
||||
)}
|
||||
</ha-chip>
|
||||
${this.addon.host_network
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
<ha-chip
|
||||
hasIcon
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_network"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiNetwork}> </ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.full_access
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
<ha-chip
|
||||
hasIcon
|
||||
@click=${this._showMoreInfo}
|
||||
id="full_access"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hardware"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiChip}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.hardware"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.homeassistant_api
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
<ha-chip
|
||||
hasIcon
|
||||
@click=${this._showMoreInfo}
|
||||
id="homeassistant_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.core"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiHomeAssistant}
|
||||
></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.core"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this._computeHassioApi
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="hassio_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||
) || this.addon.hassio_role
|
||||
)}
|
||||
>
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="hassio_api">
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiHomeAssistant}
|
||||
></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
`addon.dashboard.capability.role.${this.addon.hassio_role}`
|
||||
) || this.addon.hassio_role}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.docker_api
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="docker_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.docker"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="docker_api">
|
||||
<ha-svg-icon slot="icon" .path=${mdiDocker}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.docker"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.host_pid
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="host_pid"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host_pid"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="host_pid">
|
||||
<ha-svg-icon slot="icon" .path=${mdiPound}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.host_pid"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.apparmor !== "default"
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
<ha-chip
|
||||
hasIcon
|
||||
@click=${this._showMoreInfo}
|
||||
class=${this._computeApparmorClassName}
|
||||
id="apparmor"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.apparmor"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-svg-icon slot="icon" .path=${mdiShield}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.apparmor"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.auth_api
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="auth_api"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.auth"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="auth_api">
|
||||
<ha-svg-icon slot="icon" .path=${mdiKey}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.auth"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.ingress
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="ingress"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.ingress"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="ingress">
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiCursorDefaultClickOutline}
|
||||
></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.ingress"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
${this.addon.signed
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
filled
|
||||
@click=${this._showMoreInfo}
|
||||
id="signed"
|
||||
.label=${capitalizeFirstLetter(
|
||||
this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.signed"
|
||||
)
|
||||
)}
|
||||
>
|
||||
<ha-chip hasIcon @click=${this._showMoreInfo} id="signed">
|
||||
<ha-svg-icon slot="icon" .path=${mdiLinkLock}></ha-svg-icon>
|
||||
</ha-assist-chip>
|
||||
${this.supervisor.localize(
|
||||
"addon.dashboard.capability.label.signed"
|
||||
)}
|
||||
</ha-chip>
|
||||
`
|
||||
: ""}
|
||||
</ha-chip-set>
|
||||
@@ -1235,35 +1185,23 @@ class HassioAddonInfo extends LitElement {
|
||||
.description a {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
ha-assist-chip {
|
||||
--md-sys-color-primary: var(--text-primary-color);
|
||||
--md-sys-color-on-surface: var(--text-primary-color);
|
||||
--ha-assist-chip-filled-container-color: var(--primary-color);
|
||||
ha-chip {
|
||||
text-transform: capitalize;
|
||||
--ha-chip-text-color: var(--text-primary-color);
|
||||
--ha-chip-background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.red {
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-red,
|
||||
#df4c1e
|
||||
);
|
||||
--ha-chip-background-color: var(--label-badge-red, #df4c1e);
|
||||
}
|
||||
.blue {
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-blue,
|
||||
#039be5
|
||||
);
|
||||
--ha-chip-background-color: var(--label-badge-blue, #039be5);
|
||||
}
|
||||
.green {
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-green,
|
||||
#0da035
|
||||
);
|
||||
--ha-chip-background-color: var(--label-badge-green, #0da035);
|
||||
}
|
||||
.yellow {
|
||||
--ha-assist-chip-filled-container-color: var(
|
||||
--label-badge-yellow,
|
||||
#f4b400
|
||||
);
|
||||
--ha-chip-background-color: var(--label-badge-yellow, #f4b400);
|
||||
}
|
||||
.capabilities {
|
||||
margin-bottom: 16px;
|
||||
@@ -1322,6 +1260,9 @@ class HassioAddonInfo extends LitElement {
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
ha-chip {
|
||||
line-height: 36px;
|
||||
}
|
||||
.addon-options {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import Fuse from "fuse.js";
|
||||
import type { IFuseOptions } from "fuse.js";
|
||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
||||
|
||||
export function filterAndSort(addons: StoreAddon[], filter: string) {
|
||||
const options: IFuseOptions<StoreAddon> = {
|
||||
const options: Fuse.IFuseOptions<StoreAddon> = {
|
||||
keys: ["name", "description", "slug"],
|
||||
isCaseSensitive: false,
|
||||
minMatchCharLength: 2,
|
||||
|
||||
+54
-54
@@ -27,22 +27,22 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.23.2",
|
||||
"@braintree/sanitize-url": "6.0.4",
|
||||
"@codemirror/autocomplete": "6.11.0",
|
||||
"@codemirror/autocomplete": "6.10.2",
|
||||
"@codemirror/commands": "6.3.0",
|
||||
"@codemirror/language": "6.9.2",
|
||||
"@codemirror/legacy-modes": "6.3.3",
|
||||
"@codemirror/search": "6.5.4",
|
||||
"@codemirror/state": "6.3.1",
|
||||
"@codemirror/view": "6.22.0",
|
||||
"@codemirror/view": "6.21.4",
|
||||
"@egjs/hammerjs": "2.0.17",
|
||||
"@formatjs/intl-datetimeformat": "6.11.2",
|
||||
"@formatjs/intl-displaynames": "6.6.2",
|
||||
"@formatjs/intl-datetimeformat": "6.11.1",
|
||||
"@formatjs/intl-displaynames": "6.6.1",
|
||||
"@formatjs/intl-getcanonicallocales": "2.3.0",
|
||||
"@formatjs/intl-listformat": "7.5.1",
|
||||
"@formatjs/intl-locale": "3.4.1",
|
||||
"@formatjs/intl-numberformat": "8.8.1",
|
||||
"@formatjs/intl-pluralrules": "5.2.8",
|
||||
"@formatjs/intl-relativetimeformat": "11.2.8",
|
||||
"@formatjs/intl-listformat": "7.5.0",
|
||||
"@formatjs/intl-locale": "3.4.0",
|
||||
"@formatjs/intl-numberformat": "8.8.0",
|
||||
"@formatjs/intl-pluralrules": "5.2.7",
|
||||
"@formatjs/intl-relativetimeformat": "11.2.7",
|
||||
"@fullcalendar/core": "6.1.9",
|
||||
"@fullcalendar/daygrid": "6.1.9",
|
||||
"@fullcalendar/interaction": "6.1.9",
|
||||
@@ -51,9 +51,9 @@
|
||||
"@fullcalendar/timegrid": "6.1.9",
|
||||
"@lezer/highlight": "1.1.6",
|
||||
"@lit-labs/context": "0.4.1",
|
||||
"@lit-labs/motion": "1.0.6",
|
||||
"@lit-labs/observers": "2.0.2",
|
||||
"@lit-labs/virtualizer": "2.0.10",
|
||||
"@lit-labs/motion": "1.0.4",
|
||||
"@lit-labs/observers": "2.0.1",
|
||||
"@lit-labs/virtualizer": "2.0.7",
|
||||
"@lrnwebcomponents/simple-tooltip": "7.0.18",
|
||||
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
|
||||
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
|
||||
@@ -94,8 +94,8 @@
|
||||
"@polymer/paper-toast": "3.0.1",
|
||||
"@polymer/polymer": "3.5.1",
|
||||
"@thomasloven/round-slider": "0.6.0",
|
||||
"@vaadin/combo-box": "24.2.2",
|
||||
"@vaadin/vaadin-themable-mixin": "24.2.2",
|
||||
"@vaadin/combo-box": "24.2.1",
|
||||
"@vaadin/vaadin-themable-mixin": "24.2.1",
|
||||
"@vibrant/color": "3.2.1-alpha.1",
|
||||
"@vibrant/core": "3.2.1-alpha.1",
|
||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||
@@ -105,28 +105,28 @@
|
||||
"app-datepicker": "5.1.1",
|
||||
"chart.js": "4.4.0",
|
||||
"comlink": "4.4.1",
|
||||
"core-js": "3.33.2",
|
||||
"core-js": "3.33.1",
|
||||
"cropperjs": "1.6.1",
|
||||
"date-fns": "2.30.0",
|
||||
"date-fns-tz": "2.0.0",
|
||||
"deep-clone-simple": "1.1.1",
|
||||
"deep-freeze": "0.0.1",
|
||||
"fuse.js": "7.0.0",
|
||||
"fuse.js": "6.6.2",
|
||||
"google-timezones-json": "1.2.0",
|
||||
"hls.js": "1.4.12",
|
||||
"home-assistant-js-websocket": "9.1.0",
|
||||
"idb-keyval": "6.2.1",
|
||||
"intl-messageformat": "10.5.5",
|
||||
"intl-messageformat": "10.5.4",
|
||||
"js-yaml": "4.1.0",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-draw": "1.0.4",
|
||||
"lit": "2.8.0",
|
||||
"luxon": "3.4.3",
|
||||
"marked": "9.1.6",
|
||||
"marked": "9.1.2",
|
||||
"memoize-one": "6.0.0",
|
||||
"node-vibrant": "3.2.1-alpha.1",
|
||||
"proxy-polyfill": "0.3.2",
|
||||
"punycode": "2.3.1",
|
||||
"punycode": "2.3.0",
|
||||
"qr-scanner": "1.4.2",
|
||||
"qrcode": "1.5.3",
|
||||
"resize-observer-polyfill": "1.5.1",
|
||||
@@ -138,10 +138,10 @@
|
||||
"tinykeys": "2.1.0",
|
||||
"tsparticles-engine": "2.12.0",
|
||||
"tsparticles-preset-links": "2.12.0",
|
||||
"ua-parser-js": "1.0.37",
|
||||
"ua-parser-js": "1.0.36",
|
||||
"unfetch": "5.0.0",
|
||||
"vis-data": "7.1.8",
|
||||
"vis-network": "9.1.9",
|
||||
"vis-data": "7.1.7",
|
||||
"vis-network": "9.1.8",
|
||||
"vue": "2.7.15",
|
||||
"vue2-daterange-picker": "0.6.8",
|
||||
"weekstart": "2.0.0",
|
||||
@@ -154,12 +154,12 @@
|
||||
"xss": "1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.23.3",
|
||||
"@babel/plugin-proposal-decorators": "7.23.3",
|
||||
"@babel/plugin-transform-runtime": "7.23.3",
|
||||
"@babel/preset-env": "7.23.3",
|
||||
"@babel/preset-typescript": "7.23.3",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.8.0",
|
||||
"@babel/core": "7.23.2",
|
||||
"@babel/plugin-proposal-decorators": "7.23.2",
|
||||
"@babel/plugin-transform-runtime": "7.23.2",
|
||||
"@babel/preset-env": "7.23.2",
|
||||
"@babel/preset-typescript": "7.23.2",
|
||||
"@bundle-stats/plugin-webpack-filter": "4.7.8",
|
||||
"@koa/cors": "4.0.0",
|
||||
"@lokalise/node-api": "12.0.0",
|
||||
"@octokit/auth-oauth-device": "6.0.1",
|
||||
@@ -170,32 +170,33 @@
|
||||
"@rollup/plugin-commonjs": "25.0.7",
|
||||
"@rollup/plugin-json": "6.0.1",
|
||||
"@rollup/plugin-node-resolve": "15.2.3",
|
||||
"@rollup/plugin-replace": "5.0.5",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||
"@types/chromecast-caf-receiver": "6.0.12",
|
||||
"@types/chromecast-caf-sender": "1.0.8",
|
||||
"@rollup/plugin-replace": "5.0.4",
|
||||
"@types/babel__plugin-transform-runtime": "7.9.4",
|
||||
"@types/chromecast-caf-receiver": "6.0.11",
|
||||
"@types/chromecast-caf-sender": "1.0.7",
|
||||
"@types/esprima": "4.0.5",
|
||||
"@types/glob": "8.1.0",
|
||||
"@types/html-minifier-terser": "7.0.2",
|
||||
"@types/js-yaml": "4.0.9",
|
||||
"@types/leaflet": "1.9.8",
|
||||
"@types/leaflet-draw": "1.0.10",
|
||||
"@types/luxon": "3.3.4",
|
||||
"@types/mocha": "10.0.4",
|
||||
"@types/qrcode": "1.5.5",
|
||||
"@types/serve-handler": "6.1.4",
|
||||
"@types/sortablejs": "1.15.5",
|
||||
"@types/tar": "6.1.9",
|
||||
"@types/ua-parser-js": "0.7.39",
|
||||
"@types/html-minifier-terser": "7.0.1",
|
||||
"@types/js-yaml": "4.0.8",
|
||||
"@types/leaflet": "1.9.7",
|
||||
"@types/leaflet-draw": "1.0.9",
|
||||
"@types/luxon": "3.3.3",
|
||||
"@types/mocha": "10.0.3",
|
||||
"@types/qrcode": "1.5.4",
|
||||
"@types/serve-handler": "6.1.3",
|
||||
"@types/sortablejs": "1.15.4",
|
||||
"@types/tar": "6.1.7",
|
||||
"@types/ua-parser-js": "0.7.38",
|
||||
"@types/webspeechapi": "0.0.29",
|
||||
"@typescript-eslint/eslint-plugin": "6.10.0",
|
||||
"@typescript-eslint/parser": "6.10.0",
|
||||
"@typescript-eslint/eslint-plugin": "6.9.0",
|
||||
"@typescript-eslint/parser": "6.9.0",
|
||||
"@web/dev-server": "0.1.38",
|
||||
"@web/dev-server-rollup": "0.4.1",
|
||||
"babel-loader": "9.1.3",
|
||||
"babel-plugin-template-html-minifier": "4.1.0",
|
||||
"chai": "4.3.10",
|
||||
"del": "7.1.0",
|
||||
"eslint": "8.53.0",
|
||||
"eslint": "8.52.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-airbnb-typescript": "17.1.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
@@ -203,9 +204,10 @@
|
||||
"eslint-plugin-disable": "2.0.3",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
"eslint-plugin-lit": "1.10.1",
|
||||
"eslint-plugin-lit-a11y": "4.1.1",
|
||||
"eslint-plugin-lit-a11y": "4.1.0",
|
||||
"eslint-plugin-unused-imports": "3.0.0",
|
||||
"eslint-plugin-wc": "2.0.4",
|
||||
"esprima": "4.0.1",
|
||||
"fancy-log": "2.0.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"glob": "10.3.10",
|
||||
@@ -219,7 +221,7 @@
|
||||
"husky": "8.0.3",
|
||||
"instant-mocha": "1.5.2",
|
||||
"jszip": "3.10.1",
|
||||
"lint-staged": "15.1.0",
|
||||
"lint-staged": "15.0.2",
|
||||
"lit-analyzer": "2.0.1",
|
||||
"lodash.template": "4.5.0",
|
||||
"magic-string": "0.30.5",
|
||||
@@ -234,12 +236,12 @@
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"rollup-plugin-visualizer": "5.9.2",
|
||||
"serve-handler": "6.1.5",
|
||||
"sinon": "17.0.1",
|
||||
"sinon": "17.0.0",
|
||||
"source-map-url": "0.4.1",
|
||||
"systemjs": "6.14.2",
|
||||
"tar": "6.2.0",
|
||||
"terser-webpack-plugin": "5.3.9",
|
||||
"ts-lit-plugin": "2.0.1",
|
||||
"ts-lit-plugin": "2.0.0",
|
||||
"typescript": "5.2.2",
|
||||
"vinyl-buffer": "1.0.1",
|
||||
"vinyl-source-stream": "2.0.0",
|
||||
@@ -255,11 +257,9 @@
|
||||
"resolutions": {
|
||||
"@polymer/polymer": "patch:@polymer/polymer@3.5.1#./.yarn/patches/@polymer/polymer/pr-5569.patch",
|
||||
"@material/mwc-button@^0.25.3": "^0.27.0",
|
||||
"lit": "2.8.0",
|
||||
"clean-css": "5.3.2",
|
||||
"@lit/reactive-element": "1.6.3",
|
||||
"lit@^2.7.4 || ^3.0.0": "^2.7.4",
|
||||
"sortablejs@1.15.0": "patch:sortablejs@npm%3A1.15.0#./.yarn/patches/sortablejs-npm-1.15.0-f3a393abcc.patch",
|
||||
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch"
|
||||
},
|
||||
"packageManager": "yarn@4.0.1"
|
||||
"packageManager": "yarn@3.6.4"
|
||||
}
|
||||
|
||||
@@ -1 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="640" viewBox="0 0 240 240"><path d="M120.001 0c-3.787 0-7.573 1.499-10.444 4.49L12.211 105.905a25.921 25.921 0 0 0-2.098 2.501 35.25 35.25 0 0 0-1.96 2.942c-3.01 5.021-5.285 11.318-6.074 16.898-.03.21-.088.429-.11.636a27.355 27.355 0 0 0-.213 3.317v93.023a14.78 14.78 90 0 0 14.78 14.78h90.92L67.422 198.29a20.2 20.2 90 1 1 12.542-13.06l31.17 32.474V98.726a20.2 20.2 90 1 1 17.734 0v83.44l31.001-32.299a20.2 20.2 90 1 1 12.267 13.357l-43.269 45.082V240h94.9a14.479 14.479 90 0 0 14.478-14.479v-93.314c0-1.059-.069-2.168-.214-3.314-.7-5.73-3.06-12.327-6.183-17.537a35.801 35.801 0 0 0-1.955-2.937 26.271 26.271 0 0 0-2.102-2.506L130.444 4.486C127.573 1.494 123.786-.002 120.001 0"/></svg>
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="480.000000pt" height="480.000000pt" viewBox="0 0 480.000000 480.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,480.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M2313 4666 c-23 -7 -56 -23 -75 -34 -47 -30 -2059 -2048 -2095 -2102
|
||||
-45 -67 -77 -135 -109 -230 l-29 -85 0 -995 0 -995 27 -51 c31 -59 93 -118
|
||||
152 -145 39 -18 83 -19 1001 -19 l960 0 -406 405 c-395 395 -406 406 -433 395
|
||||
-15 -5 -63 -10 -107 -10 -429 0 -566 577 -181 767 67 34 86 38 164 42 105 4
|
||||
165 -13 246 -67 113 -74 175 -190 176 -327 1 -44 -3 -96 -7 -115 l-8 -35 316
|
||||
-315 315 -315 0 1160 -1 1160 -51 35 c-260 177 -226 567 62 704 82 39 209 48
|
||||
293 21 239 -78 354 -352 242 -575 -32 -63 -89 -125 -141 -156 l-44 -26 0 -811
|
||||
0 -812 315 315 c218 217 313 320 309 330 -14 35 -16 134 -4 190 26 122 111
|
||||
227 230 284 82 39 209 48 293 21 115 -38 214 -130 258 -242 19 -46 23 -78 24
|
||||
-153 0 -86 -3 -101 -32 -163 -40 -84 -118 -163 -198 -202 -49 -23 -77 -29
|
||||
-150 -33 -50 -2 -108 1 -130 7 l-40 11 -437 -438 -438 -437 0 -307 0 -308 998
|
||||
0 c981 0 998 1 1042 21 58 26 115 81 148 144 l27 50 0 995 0 995 -33 95 c-72
|
||||
209 -6 135 -1147 1278 -840 843 -1040 1037 -1082 1059 -64 31 -159 39 -220 19z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 747 B After Width: | Height: | Size: 1.4 KiB |
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "home-assistant-frontend"
|
||||
version = "20231030.0"
|
||||
version = "20231027.0"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "The Home Assistant frontend"
|
||||
readme = "README.md"
|
||||
|
||||
+1
-13
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"configMigration": true,
|
||||
"extends": [
|
||||
":ignoreModulesAndTests",
|
||||
":label(Dependencies)",
|
||||
@@ -11,7 +10,7 @@
|
||||
"group:recommended",
|
||||
"npm:unpublishSafe"
|
||||
],
|
||||
"enabledManagers": ["npm", "nvm"],
|
||||
"enabledManagers": ["npm"],
|
||||
"postUpdateOptions": ["yarnDedupeHighest"],
|
||||
"lockFileMaintenance": {
|
||||
"description": ["Run after patch releases but before next beta"],
|
||||
@@ -29,22 +28,11 @@
|
||||
"matchPackageNames": ["vue"],
|
||||
"allowedVersions": "< 3"
|
||||
},
|
||||
{
|
||||
"description": "Group MDI packages",
|
||||
"groupName": "Material Design Icons",
|
||||
"matchPackageNames": ["@mdi/js", "@mdi/svg"]
|
||||
},
|
||||
{
|
||||
"description": "Group tsparticles engine and presets",
|
||||
"groupName": "tsparticles",
|
||||
"matchPackageNames": ["tsparticles-engine"],
|
||||
"matchPackagePrefixes": ["tsparticles-preset-"]
|
||||
},
|
||||
{
|
||||
"description": "Group and temporarily disable WDS packages",
|
||||
"groupName": "Web Dev Server",
|
||||
"matchPackagePrefixes": ["@web/dev-server"],
|
||||
"enabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import "../components/ha-alert";
|
||||
import "../components/ha-checkbox";
|
||||
import { computeInitialHaFormData } from "../components/ha-form/compute-initial-ha-form-data";
|
||||
import "../components/ha-formfield";
|
||||
import "../components/ha-markdown";
|
||||
import { AuthProvider, autocompleteLoginFields } from "../data/auth";
|
||||
import {
|
||||
DataEntryFlowStep,
|
||||
@@ -181,13 +182,24 @@ export class HaAuthFlow extends LitElement {
|
||||
case "abort":
|
||||
return html`
|
||||
${this.localize("ui.panel.page-authorize.abort_intro")}:
|
||||
${this.localize(
|
||||
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
|
||||
)}
|
||||
<ha-markdown
|
||||
allowsvg
|
||||
breaks
|
||||
.content=${this.localize(
|
||||
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
|
||||
)}
|
||||
></ha-markdown>
|
||||
`;
|
||||
case "form":
|
||||
return html`
|
||||
${this._computeStepDescription(step)}
|
||||
${this._computeStepDescription(step)
|
||||
? html`
|
||||
<ha-markdown
|
||||
breaks
|
||||
.content=${this._computeStepDescription(step)}
|
||||
></ha-markdown>
|
||||
`
|
||||
: nothing}
|
||||
<ha-auth-form
|
||||
.data=${this._stepData}
|
||||
.schema=${autocompleteLoginFields(step.data_schema)}
|
||||
@@ -302,7 +314,13 @@ export class HaAuthFlow extends LitElement {
|
||||
private _computeStepDescription(step: DataEntryFlowStepForm) {
|
||||
const resourceKey =
|
||||
`ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${step.step_id}.description` as const;
|
||||
return this.localize(resourceKey, step.description_placeholders);
|
||||
const args: string[] = [];
|
||||
const placeholders = step.description_placeholders || {};
|
||||
Object.keys(placeholders).forEach((key) => {
|
||||
args.push(key);
|
||||
args.push(placeholders[key]);
|
||||
});
|
||||
return this.localize(resourceKey, ...args);
|
||||
}
|
||||
|
||||
private _computeLabelCallback(step: DataEntryFlowStepForm) {
|
||||
|
||||
@@ -58,10 +58,10 @@ const matchMaxScale = (
|
||||
return outputColors.map((value) => Math.round(value * factor));
|
||||
};
|
||||
|
||||
export const mired2kelvin = (miredTemperature: number) =>
|
||||
const mired2kelvin = (miredTemperature: number) =>
|
||||
Math.floor(1000000 / miredTemperature);
|
||||
|
||||
export const kelvin2mired = (kelvintTemperature: number) =>
|
||||
const kelvin2mired = (kelvintTemperature: number) =>
|
||||
Math.floor(1000000 / kelvintTemperature);
|
||||
|
||||
export const rgbww2rgb = (
|
||||
|
||||
@@ -18,7 +18,6 @@ import { blankBeforePercent } from "../translations/blank_before_percent";
|
||||
import { LocalizeFunc } from "../translations/localize";
|
||||
import { computeDomain } from "./compute_domain";
|
||||
import { computeStateDomain } from "./compute_state_domain";
|
||||
import { blankBeforeUnit } from "../translations/blank_before_unit";
|
||||
|
||||
export const computeAttributeValueDisplay = (
|
||||
localize: LocalizeFunc,
|
||||
@@ -56,12 +55,20 @@ export const computeAttributeValueDisplay = (
|
||||
unit = getWeatherUnit(config, stateObj as WeatherEntity, attribute);
|
||||
}
|
||||
|
||||
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
|
||||
unit = config.unit_system.temperature;
|
||||
if (unit === "%") {
|
||||
return `${formattedValue}${blankBeforePercent(locale)}${unit}`;
|
||||
}
|
||||
|
||||
if (unit === "°") {
|
||||
return `${formattedValue}${unit}`;
|
||||
}
|
||||
|
||||
if (unit) {
|
||||
return `${formattedValue}${blankBeforeUnit(unit, locale)}${unit}`;
|
||||
return `${formattedValue} ${unit}`;
|
||||
}
|
||||
|
||||
if (TEMPERATURE_ATTRIBUTES.has(attribute)) {
|
||||
return `${formattedValue} ${config.unit_system.temperature}`;
|
||||
}
|
||||
|
||||
return formattedValue;
|
||||
|
||||
@@ -3,13 +3,13 @@ import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
||||
import { EntityRegistryDisplayEntry } from "../../data/entity_registry";
|
||||
import { FrontendLocaleData, TimeZone } from "../../data/translation";
|
||||
import {
|
||||
UPDATE_SUPPORT_PROGRESS,
|
||||
updateIsInstallingFromAttributes,
|
||||
UPDATE_SUPPORT_PROGRESS,
|
||||
} from "../../data/update";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import {
|
||||
UNIT_TO_MILLISECOND_CONVERT,
|
||||
formatDuration,
|
||||
UNIT_TO_MILLISECOND_CONVERT,
|
||||
} from "../datetime/duration";
|
||||
import { formatDate } from "../datetime/format_date";
|
||||
import { formatDateTime } from "../datetime/format_date_time";
|
||||
@@ -19,10 +19,10 @@ import {
|
||||
getNumberFormatOptions,
|
||||
isNumericFromAttributes,
|
||||
} from "../number/format_number";
|
||||
import { blankBeforePercent } from "../translations/blank_before_percent";
|
||||
import { LocalizeFunc } from "../translations/localize";
|
||||
import { computeDomain } from "./compute_domain";
|
||||
import { supportsFeatureFromAttributes } from "./supports-feature";
|
||||
import { blankBeforeUnit } from "../translations/blank_before_unit";
|
||||
|
||||
export const computeStateDisplaySingleEntity = (
|
||||
localize: LocalizeFunc,
|
||||
@@ -108,20 +108,16 @@ export const computeStateDisplayFromEntityAttributes = (
|
||||
// fallback to default
|
||||
}
|
||||
}
|
||||
|
||||
const value = formatNumber(
|
||||
const unit = !attributes.unit_of_measurement
|
||||
? ""
|
||||
: attributes.unit_of_measurement === "%"
|
||||
? blankBeforePercent(locale) + "%"
|
||||
: ` ${attributes.unit_of_measurement}`;
|
||||
return `${formatNumber(
|
||||
state,
|
||||
locale,
|
||||
getNumberFormatOptions({ state, attributes } as HassEntity, entity)
|
||||
);
|
||||
|
||||
const unit = attributes.unit_of_measurement;
|
||||
|
||||
if (unit) {
|
||||
return `${value}${blankBeforeUnit(unit)}${unit}`;
|
||||
}
|
||||
|
||||
return value;
|
||||
)}${unit}`;
|
||||
}
|
||||
|
||||
const domain = computeDomain(entityId);
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { FrontendLocaleData } from "../../data/translation";
|
||||
import { blankBeforePercent } from "./blank_before_percent";
|
||||
|
||||
export const blankBeforeUnit = (
|
||||
unit: string,
|
||||
localeOptions?: FrontendLocaleData
|
||||
): string => {
|
||||
if (unit === "°") {
|
||||
return "";
|
||||
}
|
||||
if (localeOptions && unit === "%") {
|
||||
return blankBeforePercent(localeOptions);
|
||||
}
|
||||
return " ";
|
||||
};
|
||||
@@ -23,7 +23,6 @@ export function computeDirectionStyles(isRTL: boolean, element: LitElement) {
|
||||
}
|
||||
|
||||
export function setDirectionStyles(direction: string, element: LitElement) {
|
||||
document.dir = direction;
|
||||
element.style.direction = direction;
|
||||
element.style.setProperty("--direction", direction);
|
||||
element.style.setProperty(
|
||||
|
||||
@@ -134,7 +134,7 @@ _adapters._date.override({
|
||||
this.options.config
|
||||
);
|
||||
case "week":
|
||||
return formatDateVeryShort(
|
||||
return formatDate(
|
||||
new Date(time),
|
||||
this.options.locale,
|
||||
this.options.config
|
||||
|
||||
@@ -73,8 +73,6 @@ export class StatisticsChart extends LitElement {
|
||||
|
||||
@property({ type: Boolean }) public isLoadingData = false;
|
||||
|
||||
@property() public period?: string;
|
||||
|
||||
@state() private _chartData: ChartData = { datasets: [] };
|
||||
|
||||
@state() private _statisticIds: string[] = [];
|
||||
@@ -94,12 +92,7 @@ export class StatisticsChart extends LitElement {
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues) {
|
||||
if (
|
||||
!this.hasUpdated ||
|
||||
changedProps.has("unit") ||
|
||||
changedProps.has("period") ||
|
||||
changedProps.has("chartType")
|
||||
) {
|
||||
if (!this.hasUpdated || changedProps.has("unit")) {
|
||||
this._createOptions();
|
||||
}
|
||||
if (
|
||||
@@ -167,7 +160,6 @@ export class StatisticsChart extends LitElement {
|
||||
},
|
||||
},
|
||||
ticks: {
|
||||
source: this.chartType === "bar" ? "data" : undefined,
|
||||
maxRotation: 0,
|
||||
sampleSize: 5,
|
||||
autoSkipPadding: 20,
|
||||
@@ -181,12 +173,6 @@ export class StatisticsChart extends LitElement {
|
||||
},
|
||||
time: {
|
||||
tooltipFormat: "datetime",
|
||||
unit:
|
||||
this.chartType === "bar" &&
|
||||
this.period &&
|
||||
["hour", "day", "week", "month"].includes(this.period)
|
||||
? this.period
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import { MdAssistChip } from "@material/web/chips/assist-chip";
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
@customElement("ha-assist-chip")
|
||||
export class HaAssistChip extends MdAssistChip {
|
||||
@property({ type: Boolean, reflect: true }) filled = false;
|
||||
|
||||
static override styles = [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
--md-sys-color-primary: var(--primary-text-color);
|
||||
--md-sys-color-on-surface: var(--primary-text-color);
|
||||
--md-assist-chip-container-shape: 16px;
|
||||
--md-assist-chip-outline-color: var(--outline-color);
|
||||
--md-assist-chip-label-text-weight: 400;
|
||||
--ha-assist-chip-filled-container-color: rgba(
|
||||
var(--rgb-primary-text-color),
|
||||
0.15
|
||||
);
|
||||
}
|
||||
/** Material 3 doesn't have a filled chip, so we have to make our own **/
|
||||
.filled {
|
||||
display: flex;
|
||||
pointer-events: none;
|
||||
border-radius: inherit;
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
background-color: var(--ha-assist-chip-filled-container-color);
|
||||
}
|
||||
/** Set the size of mdc icons **/
|
||||
::slotted([slot="icon"]) {
|
||||
display: flex;
|
||||
--mdc-icon-size: var(--md-input-chip-icon-size, 18px);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
protected override renderOutline() {
|
||||
if (this.filled) {
|
||||
return html`<span class="filled"></span>`;
|
||||
}
|
||||
|
||||
return super.renderOutline();
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-assist-chip": HaAssistChip;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { MdChipSet } from "@material/web/chips/chip-set";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-chip-set")
|
||||
export class HaChipSet extends MdChipSet {}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-chip-set": HaChipSet;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { MdFilterChip } from "@material/web/chips/filter-chip";
|
||||
import { css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
@customElement("ha-filter-chip")
|
||||
export class HaFilterChip extends MdFilterChip {
|
||||
@property({ type: Boolean, reflect: true, attribute: "no-leading-icon" })
|
||||
noLeadingIcon = false;
|
||||
|
||||
static override styles = [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
--md-sys-color-primary: var(--primary-text-color);
|
||||
--md-sys-color-on-surface: var(--primary-text-color);
|
||||
--md-sys-color-on-surface-variant: var(--primary-text-color);
|
||||
--md-sys-color-on-secondary-container: var(--primary-text-color);
|
||||
--md-filter-chip-container-shape: 16px;
|
||||
--md-filter-chip-outline-color: var(--outline-color);
|
||||
--md-filter-chip-selected-container-color: rgba(
|
||||
var(--rgb-primary-text-color),
|
||||
0.15
|
||||
);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
protected renderLeadingIcon() {
|
||||
if (this.noLeadingIcon) {
|
||||
// eslint-disable-next-line lit/prefer-nothing
|
||||
return html``;
|
||||
}
|
||||
return super.renderLeadingIcon();
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-filter-chip": HaFilterChip;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { MdInputChip } from "@material/web/chips/input-chip";
|
||||
import { css } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-input-chip")
|
||||
export class HaInputChip extends MdInputChip {
|
||||
static override styles = [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
--md-sys-color-primary: var(--primary-text-color);
|
||||
--md-sys-color-on-surface: var(--primary-text-color);
|
||||
--md-sys-color-on-surface-variant: var(--primary-text-color);
|
||||
--md-sys-color-on-secondary-container: var(--primary-text-color);
|
||||
--md-input-chip-container-shape: 16px;
|
||||
--md-input-chip-outline-color: var(--outline-color);
|
||||
--md-input-chip-selected-container-color: rgba(
|
||||
var(--rgb-primary-text-color),
|
||||
0.15
|
||||
);
|
||||
}
|
||||
/** Set the size of mdc icons **/
|
||||
::slotted([slot="icon"]) {
|
||||
display: flex;
|
||||
--mdc-icon-size: var(--md-input-chip-icon-size, 18px);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-input-chip": HaInputChip;
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,8 @@ class HaEntitiesPickerLight extends LitElement {
|
||||
@property({ attribute: "picked-entity-label" })
|
||||
public pickedEntityLabel?: string;
|
||||
|
||||
@property({ attribute: "pick-entity-label" }) public pickEntityLabel?: string;
|
||||
@property({ attribute: "pick-entity-label" })
|
||||
public pickEntityLabel?: string;
|
||||
|
||||
@property() public entityFilter?: HaEntityPickerEntityFilterFunc;
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ export class HaButton extends Button {
|
||||
display: flex;
|
||||
}
|
||||
.slot-container {
|
||||
overflow: var(--button-slot-container-overflow, visible);
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// @ts-ignore
|
||||
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
unsafeCSS,
|
||||
} from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-chip-set")
|
||||
export class HaChipSet extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div class="mdc-chip-set">
|
||||
<slot></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
${unsafeCSS(chipStyles)}
|
||||
|
||||
slot::slotted(ha-chip) {
|
||||
margin: 4px 4px 4px 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-chip-set": HaChipSet;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// @ts-ignore
|
||||
import chipStyles from "@material/chips/dist/mdc.chips.min.css";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing, unsafeCSS } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
|
||||
@customElement("ha-chip")
|
||||
export class HaChip extends LitElement {
|
||||
@property({ type: Boolean }) public hasIcon = false;
|
||||
|
||||
@property({ type: Boolean }) public hasTrailingIcon = false;
|
||||
|
||||
@property({ type: Boolean }) public noText = false;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<div class="mdc-chip ${this.noText ? "no-text" : ""}">
|
||||
${this.hasIcon
|
||||
? html`<div class="mdc-chip__icon mdc-chip__icon--leading">
|
||||
<slot name="icon"></slot>
|
||||
</div>`
|
||||
: nothing}
|
||||
<div class="mdc-chip__ripple"></div>
|
||||
<span role="gridcell">
|
||||
<span role="button" tabindex="0" class="mdc-chip__primary-action">
|
||||
<span class="mdc-chip__text"><slot></slot></span>
|
||||
</span>
|
||||
</span>
|
||||
${this.hasTrailingIcon
|
||||
? html`<div class="mdc-chip__icon mdc-chip__icon--trailing">
|
||||
<slot name="trailing-icon"></slot>
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
${unsafeCSS(chipStyles)}
|
||||
.mdc-chip {
|
||||
background-color: var(
|
||||
--ha-chip-background-color,
|
||||
rgba(var(--rgb-primary-text-color), 0.15)
|
||||
);
|
||||
color: var(--ha-chip-text-color, var(--primary-text-color));
|
||||
}
|
||||
|
||||
.mdc-chip.no-text {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.mdc-chip:hover {
|
||||
color: var(--ha-chip-text-color, var(--primary-text-color));
|
||||
}
|
||||
|
||||
.mdc-chip__icon--leading,
|
||||
.mdc-chip__icon--trailing {
|
||||
--mdc-icon-size: 18px;
|
||||
line-height: 14px;
|
||||
color: var(--ha-chip-icon-color, var(--ha-chip-text-color));
|
||||
}
|
||||
.mdc-chip.mdc-chip--selected .mdc-chip__checkmark,
|
||||
.mdc-chip .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) {
|
||||
margin-right: -4px;
|
||||
margin-inline-start: -4px;
|
||||
margin-inline-end: 4px;
|
||||
direction: var(--direction);
|
||||
}
|
||||
|
||||
span[role="gridcell"] {
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
:host {
|
||||
outline: none;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-chip": HaChip;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,11 @@
|
||||
import { mdiMinus, mdiPlus } from "@mdi/js";
|
||||
import {
|
||||
CSSResultGroup,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
} from "lit";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { conditionalClamp } from "../common/number/clamp";
|
||||
import { formatNumber } from "../common/number/format_number";
|
||||
import { blankBeforeUnit } from "../common/translations/blank_before_unit";
|
||||
import { FrontendLocaleData } from "../data/translation";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
|
||||
const A11Y_KEY_CODES = new Set([
|
||||
"ArrowRight",
|
||||
@@ -42,8 +34,6 @@ export class HaControlNumberButton extends LitElement {
|
||||
|
||||
@property({ type: Number }) public max?: number;
|
||||
|
||||
@property() public unit?: string;
|
||||
|
||||
@property({ attribute: "false" })
|
||||
public formatOptions: Intl.NumberFormatOptions = {};
|
||||
|
||||
@@ -124,28 +114,26 @@ export class HaControlNumberButton extends LitElement {
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const value =
|
||||
const displayedValue =
|
||||
this.value != null
|
||||
? formatNumber(this.value, this.locale, this.formatOptions)
|
||||
: "";
|
||||
const unit = this.unit ? `${blankBeforeUnit(this.unit)}${this.unit}` : "";
|
||||
|
||||
return html`
|
||||
<div class="container">
|
||||
<div
|
||||
id="input"
|
||||
class="value"
|
||||
role="spinbutton"
|
||||
role="number-button"
|
||||
.tabIndex=${this.disabled ? "-1" : "0"}
|
||||
aria-valuenow=${this.value}
|
||||
aria-valuetext=${`${value}${unit}`}
|
||||
aria-valuemin=${this.min}
|
||||
aria-valuemax=${this.max}
|
||||
aria-label=${ifDefined(this.label)}
|
||||
?disabled=${this.disabled}
|
||||
@keydown=${this._handleKeyDown}
|
||||
>
|
||||
${value} ${unit ? html`<span class="unit">${unit}</span>` : nothing}
|
||||
${displayedValue}
|
||||
</div>
|
||||
<button
|
||||
class="button minus"
|
||||
@@ -197,8 +185,6 @@ export class HaControlNumberButton extends LitElement {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
container-type: inline-size;
|
||||
container-name: container;
|
||||
}
|
||||
.value {
|
||||
display: flex;
|
||||
@@ -263,14 +249,6 @@ export class HaControlNumberButton extends LitElement {
|
||||
.button.plus {
|
||||
right: 0;
|
||||
}
|
||||
.unit {
|
||||
white-space: pre;
|
||||
}
|
||||
@container container (max-width: 100px) {
|
||||
.unit {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,13 @@ import {
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
nothing,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { FrontendLocaleData } from "../data/translation";
|
||||
import { formatNumber } from "../common/number/format_number";
|
||||
import { blankBeforeUnit } from "../common/translations/blank_before_unit";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@@ -33,21 +29,13 @@ const A11Y_KEY_CODES = new Set([
|
||||
"End",
|
||||
]);
|
||||
|
||||
type TooltipPosition = "top" | "bottom" | "left" | "right";
|
||||
|
||||
type TooltipMode = "never" | "always" | "interaction";
|
||||
|
||||
type SliderMode = "start" | "end" | "cursor";
|
||||
|
||||
@customElement("ha-control-slider")
|
||||
export class HaControlSlider extends LitElement {
|
||||
@property({ attribute: false }) public locale?: FrontendLocaleData;
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public disabled = false;
|
||||
|
||||
@property()
|
||||
public mode?: SliderMode = "start";
|
||||
public mode?: "start" | "end" | "cursor" = "start";
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public vertical = false;
|
||||
@@ -58,15 +46,6 @@ export class HaControlSlider extends LitElement {
|
||||
@property({ type: Boolean, attribute: "inverted" })
|
||||
public inverted = false;
|
||||
|
||||
@property({ attribute: "tooltip-position" })
|
||||
public tooltipPosition?: TooltipPosition;
|
||||
|
||||
@property()
|
||||
public unit?: string;
|
||||
|
||||
@property({ attribute: "tooltip-mode" })
|
||||
public tooltipMode: TooltipMode = "interaction";
|
||||
|
||||
@property({ type: Number })
|
||||
public value?: number;
|
||||
|
||||
@@ -79,14 +58,11 @@ export class HaControlSlider extends LitElement {
|
||||
@property({ type: Number })
|
||||
public max = 100;
|
||||
|
||||
@state()
|
||||
public pressed = false;
|
||||
|
||||
@state()
|
||||
public tooltipVisible = false;
|
||||
|
||||
private _mc?: HammerManager;
|
||||
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public pressed = false;
|
||||
|
||||
valueToPercentage(value: number) {
|
||||
const percentage =
|
||||
(this.boundedValue(value) - this.min) / (this.max - this.min);
|
||||
@@ -122,7 +98,6 @@ export class HaControlSlider extends LitElement {
|
||||
if (changedProps.has("value")) {
|
||||
const valuenow = this.steppedValue(this.value ?? 0);
|
||||
this.setAttribute("aria-valuenow", valuenow.toString());
|
||||
this.setAttribute("aria-valuetext", this._formatValue(valuenow));
|
||||
}
|
||||
if (changedProps.has("min")) {
|
||||
this.setAttribute("aria-valuemin", this.min.toString());
|
||||
@@ -168,13 +143,11 @@ export class HaControlSlider extends LitElement {
|
||||
this._mc.on("panstart", () => {
|
||||
if (this.disabled) return;
|
||||
this.pressed = true;
|
||||
this._showTooltip();
|
||||
savedValue = this.value;
|
||||
});
|
||||
this._mc.on("pancancel", () => {
|
||||
if (this.disabled) return;
|
||||
this.pressed = false;
|
||||
this._hideTooltip();
|
||||
this.value = savedValue;
|
||||
});
|
||||
this._mc.on("panmove", (e) => {
|
||||
@@ -187,7 +160,6 @@ export class HaControlSlider extends LitElement {
|
||||
this._mc.on("panend", (e) => {
|
||||
if (this.disabled) return;
|
||||
this.pressed = false;
|
||||
this._hideTooltip();
|
||||
const percentage = this._getPercentageFromEvent(e);
|
||||
this.value = this.steppedValue(this.percentageToValue(percentage));
|
||||
fireEvent(this, "slider-moved", { value: undefined });
|
||||
@@ -219,21 +191,6 @@ export class HaControlSlider extends LitElement {
|
||||
return Math.max(this.step, (this.max - this.min) / 10);
|
||||
}
|
||||
|
||||
_showTooltip() {
|
||||
if (this._tooltipTimeout != null) window.clearTimeout(this._tooltipTimeout);
|
||||
this.tooltipVisible = true;
|
||||
}
|
||||
|
||||
_hideTooltip(delay?: number) {
|
||||
if (!delay) {
|
||||
this.tooltipVisible = false;
|
||||
return;
|
||||
}
|
||||
this._tooltipTimeout = window.setTimeout(() => {
|
||||
this.tooltipVisible = false;
|
||||
}, delay);
|
||||
}
|
||||
|
||||
_handleKeyDown(e: KeyboardEvent) {
|
||||
if (!A11Y_KEY_CODES.has(e.code)) return;
|
||||
e.preventDefault();
|
||||
@@ -263,16 +220,12 @@ export class HaControlSlider extends LitElement {
|
||||
this.value = this.max;
|
||||
break;
|
||||
}
|
||||
this._showTooltip();
|
||||
fireEvent(this, "slider-moved", { value: this.value });
|
||||
}
|
||||
|
||||
private _tooltipTimeout?: number;
|
||||
|
||||
_handleKeyUp(e: KeyboardEvent) {
|
||||
if (!A11Y_KEY_CODES.has(e.code)) return;
|
||||
e.preventDefault();
|
||||
this._hideTooltip(500);
|
||||
fireEvent(this, "value-changed", { value: this.value });
|
||||
}
|
||||
|
||||
@@ -289,76 +242,36 @@ export class HaControlSlider extends LitElement {
|
||||
return Math.max(Math.min(1, (x - offset) / total), 0);
|
||||
};
|
||||
|
||||
private _formatValue(value: number) {
|
||||
const formattedValue = formatNumber(value, this.locale);
|
||||
|
||||
const formattedUnit = this.unit
|
||||
? `${blankBeforeUnit(this.unit, this.locale)}${this.unit}`
|
||||
: "";
|
||||
|
||||
return `${formattedValue}${formattedUnit}`;
|
||||
}
|
||||
|
||||
private _renderTooltip() {
|
||||
if (this.tooltipMode === "never") return nothing;
|
||||
|
||||
const position = this.tooltipPosition ?? (this.vertical ? "left" : "top");
|
||||
|
||||
const visible =
|
||||
this.tooltipMode === "always" ||
|
||||
(this.tooltipVisible && this.tooltipMode === "interaction");
|
||||
|
||||
const value = this.steppedValue(this.value ?? 0);
|
||||
|
||||
return html`
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="tooltip ${classMap({
|
||||
visible,
|
||||
[position]: true,
|
||||
[this.mode ?? "start"]: true,
|
||||
"show-handle": this.showHandle,
|
||||
})}"
|
||||
>
|
||||
${this._formatValue(value)}
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div
|
||||
class="container${classMap({
|
||||
pressed: this.pressed,
|
||||
})}"
|
||||
id="slider"
|
||||
class="slider"
|
||||
style=${styleMap({
|
||||
"--value": `${this.valueToPercentage(this.value ?? 0)}`,
|
||||
})}
|
||||
>
|
||||
<div id="slider" class="slider">
|
||||
<div class="slider-track-background"></div>
|
||||
<slot name="background"></slot>
|
||||
${this.mode === "cursor"
|
||||
? this.value != null
|
||||
? html`
|
||||
<div
|
||||
class=${classMap({
|
||||
"slider-track-cursor": true,
|
||||
})}
|
||||
></div>
|
||||
`
|
||||
: null
|
||||
: html`
|
||||
<div class="slider-track-background"></div>
|
||||
<slot name="background"></slot>
|
||||
${this.mode === "cursor"
|
||||
? this.value != null
|
||||
? html`
|
||||
<div
|
||||
class=${classMap({
|
||||
"slider-track-bar": true,
|
||||
[this.mode ?? "start"]: true,
|
||||
"show-handle": this.showHandle,
|
||||
"slider-track-cursor": true,
|
||||
})}
|
||||
></div>
|
||||
`}
|
||||
</div>
|
||||
${this._renderTooltip()}
|
||||
`
|
||||
: null
|
||||
: html`
|
||||
<div
|
||||
class=${classMap({
|
||||
"slider-track-bar": true,
|
||||
[this.mode ?? "start"]: true,
|
||||
"show-handle": this.showHandle,
|
||||
})}
|
||||
></div>
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -372,7 +285,6 @@ 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;
|
||||
height: var(--control-slider-thickness);
|
||||
width: 100%;
|
||||
border-radius: var(--control-slider-border-radius);
|
||||
@@ -386,89 +298,6 @@ export class HaControlSlider extends LitElement {
|
||||
width: var(--control-slider-thickness);
|
||||
height: 100%;
|
||||
}
|
||||
.container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
--handle-size: 4px;
|
||||
--handle-margin: calc(var(--control-slider-thickness) / 8);
|
||||
}
|
||||
.tooltip {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
background-color: var(--clear-background-color);
|
||||
color: var(--primary-text-color);
|
||||
font-size: var(--control-slider-tooltip-font-size);
|
||||
border-radius: 0.8em;
|
||||
padding: 0.2em 0.4em;
|
||||
opacity: 0;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
transition:
|
||||
opacity 180ms ease-in-out,
|
||||
left 180ms ease-in-out,
|
||||
bottom 180ms ease-in-out;
|
||||
--handle-spacing: calc(2 * var(--handle-margin) + var(--handle-size));
|
||||
--slider-tooltip-margin: -4px;
|
||||
--slider-tooltip-range: 100%;
|
||||
--slider-tooltip-offset: 0px;
|
||||
--slider-tooltip-position: calc(
|
||||
min(
|
||||
max(
|
||||
var(--value) * var(--slider-tooltip-range) +
|
||||
var(--slider-tooltip-offset),
|
||||
0%
|
||||
),
|
||||
100%
|
||||
)
|
||||
);
|
||||
}
|
||||
.tooltip.start {
|
||||
--slider-tooltip-offset: calc(-0.5 * (var(--handle-spacing)));
|
||||
}
|
||||
.tooltip.end {
|
||||
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
|
||||
}
|
||||
.tooltip.cursor {
|
||||
--slider-tooltip-range: calc(100% - var(--handle-spacing));
|
||||
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
|
||||
}
|
||||
.tooltip.show-handle {
|
||||
--slider-tooltip-range: calc(100% - var(--handle-spacing));
|
||||
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
|
||||
}
|
||||
.tooltip.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
.tooltip.top {
|
||||
transform: translate3d(-50%, -100%, 0);
|
||||
top: var(--slider-tooltip-margin);
|
||||
left: 50%;
|
||||
}
|
||||
.tooltip.bottom {
|
||||
transform: translate3d(-50%, 100%, 0);
|
||||
bottom: var(--slider-tooltip-margin);
|
||||
left: 50%;
|
||||
}
|
||||
.tooltip.left {
|
||||
transform: translate3d(-100%, 50%, 0);
|
||||
bottom: 50%;
|
||||
left: var(--slider-tooltip-margin);
|
||||
}
|
||||
.tooltip.right {
|
||||
transform: translate3d(100%, 50%, 0);
|
||||
bottom: 50%;
|
||||
right: var(--slider-tooltip-margin);
|
||||
}
|
||||
:host(:not([vertical])) .tooltip.top,
|
||||
:host(:not([vertical])) .tooltip.bottom {
|
||||
left: var(--slider-tooltip-position);
|
||||
}
|
||||
:host([vertical]) .tooltip.right,
|
||||
:host([vertical]) .tooltip.left {
|
||||
bottom: var(--slider-tooltip-position);
|
||||
}
|
||||
.slider {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
@@ -499,6 +328,8 @@ export class HaControlSlider extends LitElement {
|
||||
}
|
||||
.slider .slider-track-bar {
|
||||
--border-radius: var(--control-slider-border-radius);
|
||||
--handle-size: 4px;
|
||||
--handle-margin: calc(var(--control-slider-thickness) / 8);
|
||||
--slider-size: 100%;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
@@ -601,6 +432,7 @@ export class HaControlSlider extends LitElement {
|
||||
|
||||
.slider .slider-track-cursor {
|
||||
--cursor-size: calc(var(--control-slider-thickness) / 4);
|
||||
--handle-size: 4px;
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
border-radius: var(--handle-size);
|
||||
@@ -630,11 +462,9 @@ export class HaControlSlider extends LitElement {
|
||||
height: var(--handle-size);
|
||||
width: 50%;
|
||||
}
|
||||
.pressed .tooltip {
|
||||
transition: opacity 180ms ease-in-out;
|
||||
}
|
||||
.pressed .slider-track-bar,
|
||||
.pressed .slider-track-cursor {
|
||||
|
||||
:host([pressed]) .slider-track-bar,
|
||||
:host([pressed]) .slider-track-cursor {
|
||||
transition: none;
|
||||
}
|
||||
:host(:disabled) .slider {
|
||||
|
||||
@@ -19,14 +19,11 @@ import type {
|
||||
HaFormStringData,
|
||||
HaFormStringSchema,
|
||||
} from "./types";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
const MASKED_FIELDS = ["password", "secret", "token"];
|
||||
|
||||
@customElement("ha-form-string")
|
||||
export class HaFormString extends LitElement implements HaFormElement {
|
||||
@property() public hass?: HomeAssistant;
|
||||
|
||||
@property() public schema!: HaFormStringSchema;
|
||||
|
||||
@property() public data!: HaFormStringData;
|
||||
@@ -81,11 +78,7 @@ export class HaFormString extends LitElement implements HaFormElement {
|
||||
return html`
|
||||
<ha-icon-button
|
||||
toggles
|
||||
.label=${this.hass?.localize(
|
||||
this.unmaskedPassword
|
||||
? "ui.components.selectors.text.hide_password"
|
||||
: "ui.components.selectors.text.show_password"
|
||||
) || (this.unmaskedPassword ? "Hide password" : "Show password")}
|
||||
.label=${`${this.unmaskedPassword ? "Hide" : "Show"} password`}
|
||||
@click=${this.toggleUnmaskedPassword}
|
||||
.path=${this.unmaskedPassword ? mdiEyeOff : mdiEye}
|
||||
></ha-icon-button>
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
|
||||
@customElement("ha-label")
|
||||
class HaLabel extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<span class="label">
|
||||
<slot name="icon"></slot>
|
||||
<slot></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
css`
|
||||
:host {
|
||||
--ha-label-text-color: var(--primary-text-color);
|
||||
--ha-label-icon-color: var(--primary-text-color);
|
||||
--ha-label-background-color: rgba(
|
||||
var(--rgb-primary-text-color),
|
||||
0.15
|
||||
);
|
||||
}
|
||||
.label {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
letter-spacing: 0.1px;
|
||||
vertical-align: middle;
|
||||
height: 32px;
|
||||
padding: 0 16px;
|
||||
border-radius: 18px;
|
||||
background-color: var(--ha-label-background-color);
|
||||
color: var(--ha-label-text-color);
|
||||
--mdc-icon-size: 18px;
|
||||
}
|
||||
::slotted([slot="icon"]) {
|
||||
margin-right: 8px;
|
||||
margin-left: -8px;
|
||||
display: flex;
|
||||
color: var(--ha-label-icon-color);
|
||||
}
|
||||
span {
|
||||
display: inline-flex;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-label": HaLabel;
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,18 @@ import { customElement } from "lit/decorators";
|
||||
@customElement("ha-logo-svg")
|
||||
export class HaLogoSvg extends LitElement {
|
||||
protected render(): TemplateResult {
|
||||
return html`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
|
||||
return html`<svg
|
||||
viewBox="0 0 240 240"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill="#18BCF2"
|
||||
d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"
|
||||
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
|
||||
fill="#F2F4F9"
|
||||
/>
|
||||
<path
|
||||
fill="#F2F4F9"
|
||||
d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"
|
||||
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
|
||||
fill="#18BCF2"
|
||||
/>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@ import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { titleCase } from "../common/string/title-case";
|
||||
import { fetchConfig } from "../data/lovelace/config/types";
|
||||
import { LovelaceViewRawConfig } from "../data/lovelace/config/view";
|
||||
import { HomeAssistant, PanelInfo, ValueChangedEvent } from "../types";
|
||||
import {
|
||||
fetchConfig,
|
||||
LovelaceConfig,
|
||||
LovelaceViewConfig,
|
||||
} from "../data/lovelace";
|
||||
import { ValueChangedEvent, HomeAssistant, PanelInfo } from "../types";
|
||||
import "./ha-combo-box";
|
||||
import type { HaComboBox } from "./ha-combo-box";
|
||||
import "./ha-icon";
|
||||
@@ -29,7 +32,7 @@ const rowRenderer: ComboBoxLitRenderer<NavigationItem> = (item) => html`
|
||||
|
||||
const createViewNavigationItem = (
|
||||
prefix: string,
|
||||
view: LovelaceViewRawConfig,
|
||||
view: LovelaceViewConfig,
|
||||
index: number
|
||||
) => ({
|
||||
path: `/${prefix}/${view.path ?? index}`,
|
||||
@@ -118,7 +121,7 @@ export class HaNavigationPicker extends LitElement {
|
||||
panel.url_path === "lovelace" ? null : panel.url_path,
|
||||
true
|
||||
)
|
||||
.then((config) => [panel.id, config] as [string, typeof config])
|
||||
.then((config) => [panel.id, config] as [string, LovelaceConfig])
|
||||
.catch((_) => [panel.id, undefined] as [string, undefined])
|
||||
)
|
||||
);
|
||||
@@ -132,7 +135,7 @@ export class HaNavigationPicker extends LitElement {
|
||||
|
||||
const config = panelViewConfig.get(panel.id);
|
||||
|
||||
if (!config || !("views" in config)) continue;
|
||||
if (!config) continue;
|
||||
|
||||
config.views.forEach((view, index) =>
|
||||
this.navigationItems.push(
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { ColorTempSelector } from "../../data/selector";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../ha-labeled-slider";
|
||||
import { generateColorTemperatureGradient } from "../../dialogs/more-info/components/lights/light-color-temp-picker";
|
||||
import { mired2kelvin } from "../../common/color/convert-light-color";
|
||||
|
||||
@customElement("ha-selector-color_temp")
|
||||
export class HaColorTempSelector extends LitElement {
|
||||
@@ -26,21 +22,13 @@ export class HaColorTempSelector extends LitElement {
|
||||
@property({ type: Boolean }) public required = true;
|
||||
|
||||
protected render() {
|
||||
const min = this.selector.color_temp?.min_mireds ?? 153;
|
||||
const max = this.selector.color_temp?.max_mireds ?? 500;
|
||||
|
||||
const gradient = this._generateTemperatureGradient(min, max);
|
||||
|
||||
return html`
|
||||
<ha-labeled-slider
|
||||
style=${styleMap({
|
||||
"--ha-slider-background": `linear-gradient( to var(--float-end), ${gradient})`,
|
||||
})}
|
||||
labeled
|
||||
icon="hass:thermometer"
|
||||
.caption=${this.label || ""}
|
||||
.min=${min}
|
||||
.max=${max}
|
||||
.min=${this.selector.color_temp?.min_mireds ?? 153}
|
||||
.max=${this.selector.color_temp?.max_mireds ?? 500}
|
||||
.value=${this.value}
|
||||
.disabled=${this.disabled}
|
||||
.helper=${this.helper}
|
||||
@@ -50,16 +38,22 @@ export class HaColorTempSelector extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _generateTemperatureGradient = memoizeOne(
|
||||
(min: number, max: number) =>
|
||||
generateColorTemperatureGradient(mired2kelvin(min), mired2kelvin(max))
|
||||
);
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
fireEvent(this, "value-changed", {
|
||||
value: Number((ev.detail as any).value),
|
||||
});
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-labeled-slider {
|
||||
--ha-slider-background: linear-gradient(
|
||||
to var(--float-end),
|
||||
rgb(255, 160, 0) 0%,
|
||||
white 50%,
|
||||
rgb(166, 209, 255) 100%
|
||||
);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiDrag } from "@mdi/js";
|
||||
import { mdiClose, mdiDrag } from "@mdi/js";
|
||||
import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
||||
import { customElement, property, query } from "lit/decorators";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
@@ -12,9 +12,9 @@ import type { SelectOption, SelectSelector } from "../../data/selector";
|
||||
import { sortableStyles } from "../../resources/ha-sortable-style";
|
||||
import { SortableInstance } from "../../resources/sortable";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
import "../chips/ha-chip-set";
|
||||
import "../chips/ha-input-chip";
|
||||
import "../ha-checkbox";
|
||||
import "../ha-chip";
|
||||
import "../ha-chip-set";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import "../ha-formfield";
|
||||
@@ -65,7 +65,7 @@ export class HaSelectSelector extends LitElement {
|
||||
{
|
||||
animation: 150,
|
||||
fallbackClass: "sortable-fallback",
|
||||
draggable: "ha-input-chip",
|
||||
draggable: "ha-chip",
|
||||
onChoose: (evt: SortableEvent) => {
|
||||
(evt.item as any).placeholder =
|
||||
document.createComment("sort-placeholder");
|
||||
@@ -199,31 +199,30 @@ export class HaSelectSelector extends LitElement {
|
||||
${repeat(
|
||||
value,
|
||||
(item) => item,
|
||||
(item, idx) => {
|
||||
const label =
|
||||
options.find((option) => option.value === item)?.label ||
|
||||
item;
|
||||
return html`
|
||||
<ha-input-chip
|
||||
(item, idx) => html`
|
||||
<ha-chip
|
||||
hasTrailingIcon
|
||||
.hasIcon=${this.selector.select?.reorder}
|
||||
>
|
||||
${this.selector.select?.reorder
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiDrag}
|
||||
data-handle
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: nothing}
|
||||
${options.find((option) => option.value === item)
|
||||
?.label || item}
|
||||
<ha-svg-icon
|
||||
slot="trailing-icon"
|
||||
.path=${mdiClose}
|
||||
.idx=${idx}
|
||||
@remove=${this._removeItem}
|
||||
.label=${label}
|
||||
selected
|
||||
>
|
||||
${this.selector.select?.reorder
|
||||
? html`
|
||||
<ha-svg-icon
|
||||
slot="icon"
|
||||
.path=${mdiDrag}
|
||||
data-handle
|
||||
></ha-svg-icon>
|
||||
`
|
||||
: nothing}
|
||||
${options.find((option) => option.value === item)
|
||||
?.label || item}
|
||||
</ha-input-chip>
|
||||
`;
|
||||
}
|
||||
@click=${this._removeItem}
|
||||
></ha-svg-icon>
|
||||
</ha-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
`
|
||||
@@ -355,7 +354,6 @@ export class HaSelectSelector extends LitElement {
|
||||
}
|
||||
|
||||
private async _removeItem(ev) {
|
||||
ev.stopPropagation();
|
||||
const value: string[] = [...ensureArray(this.value!)];
|
||||
value.splice(ev.target.idx, 1);
|
||||
|
||||
@@ -433,9 +431,6 @@ export class HaSelectSelector extends LitElement {
|
||||
mwc-list-item[disabled] {
|
||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||
}
|
||||
ha-chip-set {
|
||||
padding: 8px 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -76,11 +76,7 @@ export class HaTextSelector extends LitElement {
|
||||
${this.selector.text?.type === "password"
|
||||
? html`<ha-icon-button
|
||||
toggles
|
||||
.label=${this.hass.localize(
|
||||
this._unmaskedPassword
|
||||
? "ui.components.selectors.text.hide_password"
|
||||
: "ui.components.selectors.text.show_password"
|
||||
)}
|
||||
.label=${`${this._unmaskedPassword ? "Hide" : "Show"} password`}
|
||||
@click=${this._toggleUnmaskedPassword}
|
||||
.path=${this._unmaskedPassword ? mdiEyeOff : mdiEye}
|
||||
></ha-icon-button>`
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { Trigger } from "../../data/automation";
|
||||
import { TriggerSelector } from "../../data/selector";
|
||||
import "../../panels/config/automation/trigger/ha-automation-trigger";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
||||
@customElement("ha-selector-trigger")
|
||||
export class HaTriggerSelector extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public selector!: TriggerSelector;
|
||||
|
||||
@property() public value?: Trigger;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public disabled = false;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
${this.label ? html`<label>${this.label}</label>` : nothing}
|
||||
<ha-automation-trigger
|
||||
.disabled=${this.disabled}
|
||||
.triggers=${this.value || []}
|
||||
.hass=${this.hass}
|
||||
.nested=${this.selector.trigger?.nested}
|
||||
.reOrderMode=${this.selector.trigger?.reorder_mode}
|
||||
></ha-automation-trigger>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
ha-automation-trigger {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
:host([disabled]) ha-automation-trigger {
|
||||
opacity: var(--light-disabled-opacity);
|
||||
pointer-events: none;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-selector-trigger": HaTriggerSelector;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { ActionConfig } from "../../data/lovelace/config/action";
|
||||
import { UiActionSelector } from "../../data/selector";
|
||||
import "../../panels/lovelace/components/hui-action-editor";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../../panels/lovelace/components/hui-action-editor";
|
||||
import { ActionConfig } from "../../data/lovelace";
|
||||
|
||||
@customElement("ha-selector-ui_action")
|
||||
export class HaSelectorUiAction extends LitElement {
|
||||
@@ -25,7 +25,6 @@ export class HaSelectorUiAction extends LitElement {
|
||||
.hass=${this.hass}
|
||||
.config=${this.value}
|
||||
.actions=${this.selector.ui_action?.actions}
|
||||
.defaultAction=${this.selector.ui_action?.default_action}
|
||||
.tooltipText=${this.helper}
|
||||
@value-changed=${this._valueChanged}
|
||||
></hui-action-editor>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { html, LitElement } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { ActionConfig } from "../../data/lovelace";
|
||||
import { UiColorSelector } from "../../data/selector";
|
||||
import "../../panels/lovelace/components/hui-color-picker";
|
||||
import { HomeAssistant } from "../../types";
|
||||
@@ -11,7 +12,7 @@ export class HaSelectorUiColor extends LitElement {
|
||||
|
||||
@property() public selector!: UiColorSelector;
|
||||
|
||||
@property() public value?: string;
|
||||
@property() public value?: ActionConfig;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { UserSelector } from "../../data/selector";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../user/ha-user-picker";
|
||||
import "../user/ha-users-picker";
|
||||
|
||||
@customElement("ha-selector-user")
|
||||
export class HaUserSelector extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public selector!: UserSelector;
|
||||
|
||||
@property() public value?: any;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public required = true;
|
||||
|
||||
protected render() {
|
||||
if (this.selector.user?.multiple) {
|
||||
return html`
|
||||
<ha-users-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this.value}
|
||||
.label=${this.label}
|
||||
.helper=${this.helper}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
.includeSystem=${this.selector.user.include_system}
|
||||
></ha-users-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-user-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this.value}
|
||||
.label=${this.label}
|
||||
.helper=${this.helper}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
.includeSystem=${this.selector.user?.include_system}
|
||||
></ha-user-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
ha-user-picker {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-selector-user": HaUserSelector;
|
||||
}
|
||||
}
|
||||
@@ -44,13 +44,13 @@ const LOAD_ELEMENTS = {
|
||||
icon: () => import("./ha-selector-icon"),
|
||||
media: () => import("./ha-selector-media"),
|
||||
theme: () => import("./ha-selector-theme"),
|
||||
trigger: () => import("./ha-selector-trigger"),
|
||||
tts: () => import("./ha-selector-tts"),
|
||||
tts_voice: () => import("./ha-selector-tts-voice"),
|
||||
location: () => import("./ha-selector-location"),
|
||||
color_temp: () => import("./ha-selector-color-temp"),
|
||||
ui_action: () => import("./ha-selector-ui-action"),
|
||||
ui_color: () => import("./ha-selector-ui-color"),
|
||||
user: () => import("./ha-selector-user"),
|
||||
};
|
||||
|
||||
const LEGACY_UI_SELECTORS = new Set(["ui-action", "ui-color"]);
|
||||
|
||||
@@ -41,7 +41,7 @@ import { toggleAttribute } from "../common/dom/toggle_attribute";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import { computeRTL } from "../common/util/compute_rtl";
|
||||
import { throttle } from "../common/util/throttle";
|
||||
import { ActionHandlerDetail } from "../data/lovelace/action_handler";
|
||||
import { ActionHandlerDetail } from "../data/lovelace";
|
||||
import {
|
||||
PersistentNotification,
|
||||
subscribeNotifications,
|
||||
|
||||
@@ -899,7 +899,6 @@ export class HaMediaPlayerBrowse extends LitElement {
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* HEADER */
|
||||
|
||||
@@ -1,68 +1,84 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { property } from "lit/decorators";
|
||||
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { property, query, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { stringCompare } from "../../common/string/compare";
|
||||
import {
|
||||
fuzzyFilterSort,
|
||||
ScorableTextItem,
|
||||
} from "../../common/string/filter/sequence-matching";
|
||||
import { fetchUsers, User } from "../../data/user";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { HomeAssistant, ValueChangedEvent } from "../../types";
|
||||
import "../ha-combo-box";
|
||||
import type { HaComboBox } from "../ha-combo-box";
|
||||
import "../ha-list-item";
|
||||
import "../ha-select";
|
||||
import "./ha-user-badge";
|
||||
import "../ha-list-item";
|
||||
|
||||
type ScorableUser = ScorableTextItem & User;
|
||||
|
||||
class HaUserPicker extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public noUserLabel?: string;
|
||||
@property() public value?: string;
|
||||
|
||||
@property() public value = "";
|
||||
@property() public helper?: string;
|
||||
|
||||
@property() public users?: User[];
|
||||
@property({ attribute: false }) public users?: User[];
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
@property({ type: Boolean }) public disabled?: boolean;
|
||||
|
||||
private _sortedUsers = memoizeOne((users?: User[]) => {
|
||||
if (!users) {
|
||||
return [];
|
||||
@property({ type: Boolean }) public required?: boolean;
|
||||
|
||||
@state() private _opened?: boolean;
|
||||
|
||||
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
||||
|
||||
@property({ type: Boolean, attribute: "include-system" })
|
||||
public includeSystem?: boolean;
|
||||
|
||||
private _init = false;
|
||||
|
||||
private _getUserItems = memoizeOne(
|
||||
(users: User[] | undefined): ScorableUser[] => {
|
||||
if (!users) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return users
|
||||
.sort((a, b) =>
|
||||
stringCompare(a.name, b.name, this.hass!.locale.language)
|
||||
)
|
||||
.map((user) => ({
|
||||
...user,
|
||||
strings: [user.name, ...(user.username ? [user.username] : [])],
|
||||
}));
|
||||
}
|
||||
);
|
||||
|
||||
return users
|
||||
.filter((user) => !user.system_generated)
|
||||
.sort((a, b) =>
|
||||
stringCompare(a.name, b.name, this.hass!.locale.language)
|
||||
);
|
||||
});
|
||||
private _filteredUsers = memoizeOne(
|
||||
(users: User[], includeSystem?: boolean) =>
|
||||
users.filter((user) => includeSystem || !user.system_generated)
|
||||
);
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-select
|
||||
.label=${this.label}
|
||||
.disabled=${this.disabled}
|
||||
.value=${this.value}
|
||||
@selected=${this._userChanged}
|
||||
>
|
||||
${this.users?.length === 0
|
||||
? html`<mwc-list-item value="">
|
||||
${this.noUserLabel ||
|
||||
this.hass?.localize("ui.components.user-picker.no_user")}
|
||||
</mwc-list-item>`
|
||||
: ""}
|
||||
${this._sortedUsers(this.users).map(
|
||||
(user) => html`
|
||||
<ha-list-item graphic="avatar" .value=${user.id}>
|
||||
<ha-user-badge
|
||||
.hass=${this.hass}
|
||||
.user=${user}
|
||||
slot="graphic"
|
||||
></ha-user-badge>
|
||||
${user.name}
|
||||
</ha-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
`;
|
||||
public async open() {
|
||||
await this.updateComplete;
|
||||
await this.comboBox?.open();
|
||||
}
|
||||
|
||||
public async focus() {
|
||||
await this.updateComplete;
|
||||
await this.comboBox?.focus();
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
@@ -74,25 +90,104 @@ class HaUserPicker extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _userChanged(ev) {
|
||||
const newValue = ev.target.value;
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (
|
||||
(!this._init && this.users) ||
|
||||
(this._init && changedProps.has("_opened") && this._opened)
|
||||
) {
|
||||
this._init = true;
|
||||
const filteredUsers = this._filteredUsers(
|
||||
this.users ?? [],
|
||||
this.includeSystem
|
||||
);
|
||||
const items = this._getUserItems(filteredUsers);
|
||||
|
||||
if (newValue !== this.value) {
|
||||
this.value = newValue;
|
||||
setTimeout(() => {
|
||||
fireEvent(this, "value-changed", { value: newValue });
|
||||
fireEvent(this, "change");
|
||||
}, 0);
|
||||
this.comboBox.items = items;
|
||||
this.comboBox.filteredItems = items;
|
||||
}
|
||||
}
|
||||
|
||||
private _rowRenderer: ComboBoxLitRenderer<User> = (item) => html`
|
||||
<ha-list-item
|
||||
graphic="avatar"
|
||||
.value=${item.id}
|
||||
.twoline=${!!item.username}
|
||||
>
|
||||
<ha-user-badge
|
||||
.hass=${this.hass}
|
||||
.user=${item}
|
||||
slot="graphic"
|
||||
></ha-user-badge>
|
||||
${item.name}
|
||||
<span slot="secondary">${item.username}</span>
|
||||
</ha-list-item>
|
||||
`;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-combo-box
|
||||
.hass=${this.hass}
|
||||
.label=${this.label === undefined && this.hass
|
||||
? this.hass.localize("ui.components.user-picker.user")
|
||||
: this.label}
|
||||
.value=${this._value}
|
||||
.helper=${this.helper}
|
||||
.renderer=${this._rowRenderer}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
item-id-path="id"
|
||||
item-value-path="id"
|
||||
item-label-path="name"
|
||||
@opened-changed=${this._openedChanged}
|
||||
@value-changed=${this._valueChanged}
|
||||
@filter-changed=${this._filterChanged}
|
||||
>
|
||||
</ha-combo-box>
|
||||
`;
|
||||
}
|
||||
|
||||
private _filterChanged(ev: CustomEvent): void {
|
||||
const target = ev.target as HaComboBox;
|
||||
const filterString = ev.detail.value.toLowerCase();
|
||||
target.filteredItems = filterString.length
|
||||
? fuzzyFilterSort<ScorableUser>(filterString, target.items || [])
|
||||
: target.items;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: ValueChangedEvent<string>) {
|
||||
ev.stopPropagation();
|
||||
let newValue = ev.detail.value;
|
||||
|
||||
if (newValue === "no_users") {
|
||||
newValue = "";
|
||||
}
|
||||
|
||||
if (newValue !== this._value) {
|
||||
this._setValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
private _openedChanged(ev: ValueChangedEvent<boolean>) {
|
||||
this._opened = ev.detail.value;
|
||||
}
|
||||
|
||||
private get _value() {
|
||||
return this.value || "";
|
||||
}
|
||||
|
||||
private _setValue(value: string) {
|
||||
this.value = value;
|
||||
setTimeout(() => {
|
||||
fireEvent(this, "value-changed", { value });
|
||||
fireEvent(this, "change");
|
||||
}, 0);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: inline-block;
|
||||
}
|
||||
mwc-list {
|
||||
display: block;
|
||||
ha-select {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { guard } from "lit/directives/guard";
|
||||
import memoizeOne from "memoize-one";
|
||||
@@ -15,6 +14,10 @@ class HaUsersPickerLight extends LitElement {
|
||||
|
||||
@property() public value?: string[];
|
||||
|
||||
@property({ type: Boolean }) public disabled?: boolean;
|
||||
|
||||
@property({ type: Boolean }) public required?: boolean;
|
||||
|
||||
@property({ attribute: "picked-user-label" })
|
||||
public pickedUserLabel?: string;
|
||||
|
||||
@@ -24,6 +27,9 @@ class HaUsersPickerLight extends LitElement {
|
||||
@property({ attribute: false })
|
||||
public users?: User[];
|
||||
|
||||
@property({ type: Boolean, attribute: "include-system" })
|
||||
public includeSystem?: boolean;
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
if (this.users === undefined) {
|
||||
@@ -38,69 +44,80 @@ class HaUsersPickerLight extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const notSelectedUsers = this._notSelectedUsers(this.users, this.value);
|
||||
const filteredUsers = this._filteredUsers(this.users, this.includeSystem);
|
||||
const selectedUsers = this._selectedUsers(filteredUsers, this.value);
|
||||
const notSelectedUsers = this._notSelectedUsers(filteredUsers, this.value);
|
||||
|
||||
return html`
|
||||
${guard(
|
||||
[notSelectedUsers],
|
||||
() =>
|
||||
this.value?.map(
|
||||
(user_id, idx) => html`
|
||||
<div>
|
||||
<ha-user-picker
|
||||
.label=${this.pickedUserLabel}
|
||||
.noUserLabel=${this.hass!.localize(
|
||||
"ui.components.user-picker.remove_user"
|
||||
)}
|
||||
.index=${idx}
|
||||
.hass=${this.hass}
|
||||
.value=${user_id}
|
||||
.users=${this._notSelectedUsersAndSelected(
|
||||
user_id,
|
||||
this.users,
|
||||
notSelectedUsers
|
||||
)}
|
||||
@value-changed=${this._userChanged}
|
||||
></ha-user-picker>
|
||||
<ha-icon-button
|
||||
.userId=${user_id}
|
||||
.label=${this.hass!.localize(
|
||||
"ui.components.user-picker.remove_user"
|
||||
)}
|
||||
.path=${mdiClose}
|
||||
@click=${this._removeUser}
|
||||
>
|
||||
></ha-icon-button
|
||||
>
|
||||
</div>
|
||||
`
|
||||
)
|
||||
${guard([notSelectedUsers], () =>
|
||||
selectedUsers.map(
|
||||
(user, idx) => html`
|
||||
<div>
|
||||
<ha-user-picker
|
||||
.label=${this.pickedUserLabel}
|
||||
.index=${idx}
|
||||
.hass=${this.hass}
|
||||
.value=${user.id}
|
||||
.users=${this._notSelectedUsersAndCurrent(
|
||||
user,
|
||||
filteredUsers,
|
||||
notSelectedUsers
|
||||
)}
|
||||
@value-changed=${this._userChanged}
|
||||
.disabled=${this.disabled}
|
||||
.includeSystem=${this.includeSystem}
|
||||
></ha-user-picker>
|
||||
</div>
|
||||
`
|
||||
)
|
||||
)}
|
||||
<div>${this._renderPicker(notSelectedUsers)}</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _renderPicker(users?: User[]) {
|
||||
return html`
|
||||
<ha-user-picker
|
||||
.label=${this.pickUserLabel ||
|
||||
this.hass!.localize("ui.components.user-picker.add_user")}
|
||||
.label=${this.pickUserLabel}
|
||||
.hass=${this.hass}
|
||||
.users=${notSelectedUsers}
|
||||
.disabled=${!notSelectedUsers?.length}
|
||||
.users=${users}
|
||||
@value-changed=${this._addUser}
|
||||
.disabled=${this.disabled || users?.length === 0}
|
||||
.required=${this.required}
|
||||
.includeSystem=${this.includeSystem}
|
||||
></ha-user-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
private _notSelectedUsers = memoizeOne(
|
||||
(users?: User[], currentUsers?: string[]) =>
|
||||
currentUsers
|
||||
? users?.filter(
|
||||
(user) => !user.system_generated && !currentUsers.includes(user.id)
|
||||
)
|
||||
: users?.filter((user) => !user.system_generated)
|
||||
private _filteredUsers = memoizeOne(
|
||||
(users: User[], includeSystem?: boolean) =>
|
||||
users.filter((user) => includeSystem || !user.system_generated)
|
||||
);
|
||||
|
||||
private _notSelectedUsersAndSelected = (
|
||||
userId: string,
|
||||
private _selectedUsers = memoizeOne(
|
||||
(users: User[], selectedUserIds?: string[]) => {
|
||||
if (!selectedUserIds) {
|
||||
return [];
|
||||
}
|
||||
return users.filter((user) => selectedUserIds.includes(user.id));
|
||||
}
|
||||
);
|
||||
|
||||
private _notSelectedUsers = memoizeOne(
|
||||
(users: User[], selectedUserIds?: string[]) => {
|
||||
if (!selectedUserIds) {
|
||||
return users;
|
||||
}
|
||||
return users.filter((user) => !selectedUserIds.includes(user.id));
|
||||
}
|
||||
);
|
||||
|
||||
private _notSelectedUsersAndCurrent = (
|
||||
currentUser: User,
|
||||
users?: User[],
|
||||
notSelected?: User[]
|
||||
) => {
|
||||
const selectedUser = users?.find((user) => user.id === userId);
|
||||
const selectedUser = users?.find((user) => user.id === currentUser.id);
|
||||
if (selectedUser) {
|
||||
return notSelected ? [...notSelected, selectedUser] : [selectedUser];
|
||||
}
|
||||
@@ -123,7 +140,7 @@ class HaUsersPickerLight extends LitElement {
|
||||
const index = (event.currentTarget as any).index;
|
||||
const newValue = event.detail.value;
|
||||
const newUsers = [...this._currentUsers];
|
||||
if (newValue === "") {
|
||||
if (newValue === undefined) {
|
||||
newUsers.splice(index, 1);
|
||||
} else {
|
||||
newUsers.splice(index, 1, newValue);
|
||||
@@ -146,22 +163,11 @@ class HaUsersPickerLight extends LitElement {
|
||||
this._updateUsers([...currentUsers, toAdd]);
|
||||
}
|
||||
|
||||
private _removeUser(event) {
|
||||
const userId = (event.currentTarget as any).userId;
|
||||
this._updateUsers(this._currentUsers.filter((user) => user !== userId));
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
`;
|
||||
}
|
||||
static override styles = css`
|
||||
div {
|
||||
margin-top: 8px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
+19
-32
@@ -642,7 +642,10 @@ const tryDescribeTrigger = (
|
||||
}
|
||||
|
||||
// Device Trigger
|
||||
if (trigger.platform === "device" && trigger.device_id) {
|
||||
if (trigger.platform === "device") {
|
||||
if (!trigger.device_id) {
|
||||
return "Device trigger";
|
||||
}
|
||||
const config = trigger as DeviceTrigger;
|
||||
const localized = localizeDeviceAutomationTrigger(
|
||||
hass,
|
||||
@@ -658,12 +661,9 @@ const tryDescribeTrigger = (
|
||||
}`;
|
||||
}
|
||||
|
||||
return (
|
||||
hass.localize(
|
||||
`ui.panel.config.automation.editor.triggers.type.${trigger.platform}.label`
|
||||
) ||
|
||||
hass.localize(`ui.panel.config.automation.editor.triggers.unknown_trigger`)
|
||||
);
|
||||
return `${
|
||||
trigger.platform ? trigger.platform.replace(/_/g, " ") : "Unknown"
|
||||
} trigger`;
|
||||
};
|
||||
|
||||
export const describeCondition = (
|
||||
@@ -1074,7 +1074,10 @@ const tryDescribeCondition = (
|
||||
);
|
||||
}
|
||||
|
||||
if (condition.condition === "device" && condition.device_id) {
|
||||
if (condition.condition === "device") {
|
||||
if (!condition.device_id) {
|
||||
return "Device condition";
|
||||
}
|
||||
const config = condition as DeviceCondition;
|
||||
const localized = localizeDeviceAutomationCondition(
|
||||
hass,
|
||||
@@ -1090,30 +1093,14 @@ const tryDescribeCondition = (
|
||||
}`;
|
||||
}
|
||||
|
||||
if (condition.condition === "template") {
|
||||
return hass.localize(
|
||||
`${conditionsTranslationBaseKey}.template.description.full`
|
||||
);
|
||||
if (condition.condition === "trigger") {
|
||||
if (!condition.id) {
|
||||
return "Trigger condition";
|
||||
}
|
||||
return `When triggered by ${condition.id}`;
|
||||
}
|
||||
|
||||
if (condition.condition === "trigger" && condition.id != null) {
|
||||
return hass.localize(
|
||||
`${conditionsTranslationBaseKey}.trigger.description.full`,
|
||||
{
|
||||
id: formatListWithOrs(
|
||||
hass.locale,
|
||||
ensureArray(condition.id).map((id) => id.toString())
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
hass.localize(
|
||||
`ui.panel.config.automation.editor.conditions.type.${condition.condition}.label`
|
||||
) ||
|
||||
hass.localize(
|
||||
`ui.panel.config.automation.editor.conditions.unknown_condition`
|
||||
)
|
||||
);
|
||||
return `${
|
||||
condition.condition ? condition.condition.replace(/_/g, " ") : "Unknown"
|
||||
} condition`;
|
||||
};
|
||||
|
||||
@@ -139,8 +139,7 @@ export const getCalendars = (hass: HomeAssistant): Calendar[] =>
|
||||
.filter(
|
||||
(eid) =>
|
||||
computeDomain(eid) === "calendar" &&
|
||||
!isUnavailableState(hass.states[eid].state) &&
|
||||
hass.entities[eid]?.hidden !== true
|
||||
!isUnavailableState(hass.states[eid].state)
|
||||
)
|
||||
.sort()
|
||||
.map((eid, idx) => ({
|
||||
|
||||
@@ -34,7 +34,7 @@ export const TEMPERATURE_ATTRIBUTES = new Set([
|
||||
"max_temp",
|
||||
]);
|
||||
|
||||
export const DOMAIN_ATTRIBUTES_UNITS = {
|
||||
export const DOMAIN_ATTRIBUTES_UNITS: Record<string, Record<string, string>> = {
|
||||
climate: {
|
||||
humidity: "%",
|
||||
current_humidity: "%",
|
||||
@@ -74,4 +74,4 @@ export const DOMAIN_ATTRIBUTES_UNITS = {
|
||||
sensor: {
|
||||
battery_level: "%",
|
||||
},
|
||||
} as const satisfies Record<string, Record<string, string>>;
|
||||
};
|
||||
|
||||
+272
-3
@@ -2,7 +2,9 @@ import {
|
||||
Connection,
|
||||
getCollection,
|
||||
HassEventBase,
|
||||
HassServiceTarget,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { HASSDomEvent } from "../common/dom/fire_event";
|
||||
import { HuiErrorCard } from "../panels/lovelace/cards/hui-error-card";
|
||||
import {
|
||||
Lovelace,
|
||||
@@ -10,13 +12,90 @@ import {
|
||||
LovelaceCard,
|
||||
} from "../panels/lovelace/types";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { fetchConfig, LegacyLovelaceConfig } from "./lovelace/config/types";
|
||||
import { LovelaceViewConfig } from "./lovelace/config/view";
|
||||
|
||||
export interface LovelacePanelConfig {
|
||||
mode: "yaml" | "storage";
|
||||
}
|
||||
|
||||
export type LovelaceStrategyConfig = {
|
||||
type: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
export interface LovelaceConfig {
|
||||
title?: string;
|
||||
strategy?: LovelaceStrategyConfig;
|
||||
views: LovelaceViewConfig[];
|
||||
background?: string;
|
||||
}
|
||||
|
||||
export interface LegacyLovelaceConfig extends LovelaceConfig {
|
||||
resources?: LovelaceResource[];
|
||||
}
|
||||
|
||||
export interface LovelaceResource {
|
||||
id: string;
|
||||
type: "css" | "js" | "module" | "html";
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface LovelaceResourcesMutableParams {
|
||||
res_type: LovelaceResource["type"];
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type LovelaceDashboard =
|
||||
| LovelaceYamlDashboard
|
||||
| LovelaceStorageDashboard;
|
||||
|
||||
interface LovelaceGenericDashboard {
|
||||
id: string;
|
||||
url_path: string;
|
||||
require_admin: boolean;
|
||||
show_in_sidebar: boolean;
|
||||
icon?: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface LovelaceYamlDashboard extends LovelaceGenericDashboard {
|
||||
mode: "yaml";
|
||||
filename: string;
|
||||
}
|
||||
|
||||
export interface LovelaceStorageDashboard extends LovelaceGenericDashboard {
|
||||
mode: "storage";
|
||||
}
|
||||
|
||||
export interface LovelaceDashboardMutableParams {
|
||||
require_admin: boolean;
|
||||
show_in_sidebar: boolean;
|
||||
icon?: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface LovelaceDashboardCreateParams
|
||||
extends LovelaceDashboardMutableParams {
|
||||
url_path: string;
|
||||
mode: "storage";
|
||||
}
|
||||
|
||||
export interface LovelaceViewConfig {
|
||||
index?: number;
|
||||
title?: string;
|
||||
type?: string;
|
||||
strategy?: LovelaceStrategyConfig;
|
||||
badges?: Array<string | LovelaceBadgeConfig>;
|
||||
cards?: LovelaceCardConfig[];
|
||||
path?: string;
|
||||
icon?: string;
|
||||
theme?: string;
|
||||
panel?: boolean;
|
||||
background?: string;
|
||||
visible?: boolean | ShowViewConfig[];
|
||||
subview?: boolean;
|
||||
back_path?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceViewElement extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
lovelace?: Lovelace;
|
||||
@@ -28,6 +107,89 @@ export interface LovelaceViewElement extends HTMLElement {
|
||||
setConfig(config: LovelaceViewConfig): void;
|
||||
}
|
||||
|
||||
export interface ShowViewConfig {
|
||||
user?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceBadgeConfig {
|
||||
type?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface LovelaceCardConfig {
|
||||
index?: number;
|
||||
view_index?: number;
|
||||
view_layout?: any;
|
||||
type: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface ToggleActionConfig extends BaseActionConfig {
|
||||
action: "toggle";
|
||||
}
|
||||
|
||||
export interface CallServiceActionConfig extends BaseActionConfig {
|
||||
action: "call-service";
|
||||
service: string;
|
||||
target?: HassServiceTarget;
|
||||
// "service_data" is kept for backwards compatibility. Replaced by "data".
|
||||
service_data?: Record<string, unknown>;
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface NavigateActionConfig extends BaseActionConfig {
|
||||
action: "navigate";
|
||||
navigation_path: string;
|
||||
navigation_replace?: boolean;
|
||||
}
|
||||
|
||||
export interface UrlActionConfig extends BaseActionConfig {
|
||||
action: "url";
|
||||
url_path: string;
|
||||
}
|
||||
|
||||
export interface MoreInfoActionConfig extends BaseActionConfig {
|
||||
action: "more-info";
|
||||
}
|
||||
|
||||
export interface AssistActionConfig extends BaseActionConfig {
|
||||
action: "assist";
|
||||
pipeline_id?: string;
|
||||
start_listening?: boolean;
|
||||
}
|
||||
|
||||
export interface NoActionConfig extends BaseActionConfig {
|
||||
action: "none";
|
||||
}
|
||||
|
||||
export interface CustomActionConfig extends BaseActionConfig {
|
||||
action: "fire-dom-event";
|
||||
}
|
||||
|
||||
export interface BaseActionConfig {
|
||||
action: string;
|
||||
confirmation?: ConfirmationRestrictionConfig;
|
||||
}
|
||||
|
||||
export interface ConfirmationRestrictionConfig {
|
||||
text?: string;
|
||||
exemptions?: RestrictionConfig[];
|
||||
}
|
||||
|
||||
export interface RestrictionConfig {
|
||||
user: string;
|
||||
}
|
||||
|
||||
export type ActionConfig =
|
||||
| ToggleActionConfig
|
||||
| CallServiceActionConfig
|
||||
| NavigateActionConfig
|
||||
| UrlActionConfig
|
||||
| MoreInfoActionConfig
|
||||
| AssistActionConfig
|
||||
| NoActionConfig
|
||||
| CustomActionConfig;
|
||||
|
||||
type LovelaceUpdatedEvent = HassEventBase & {
|
||||
event_type: "lovelace_updated";
|
||||
data: {
|
||||
@@ -36,6 +198,101 @@ type LovelaceUpdatedEvent = HassEventBase & {
|
||||
};
|
||||
};
|
||||
|
||||
export const fetchResources = (conn: Connection): Promise<LovelaceResource[]> =>
|
||||
conn.sendMessagePromise({
|
||||
type: "lovelace/resources",
|
||||
});
|
||||
|
||||
export const createResource = (
|
||||
hass: HomeAssistant,
|
||||
values: LovelaceResourcesMutableParams
|
||||
) =>
|
||||
hass.callWS<LovelaceResource>({
|
||||
type: "lovelace/resources/create",
|
||||
...values,
|
||||
});
|
||||
|
||||
export const updateResource = (
|
||||
hass: HomeAssistant,
|
||||
id: string,
|
||||
updates: Partial<LovelaceResourcesMutableParams>
|
||||
) =>
|
||||
hass.callWS<LovelaceResource>({
|
||||
type: "lovelace/resources/update",
|
||||
resource_id: id,
|
||||
...updates,
|
||||
});
|
||||
|
||||
export const deleteResource = (hass: HomeAssistant, id: string) =>
|
||||
hass.callWS({
|
||||
type: "lovelace/resources/delete",
|
||||
resource_id: id,
|
||||
});
|
||||
|
||||
export const fetchDashboards = (
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceDashboard[]> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/dashboards/list",
|
||||
});
|
||||
|
||||
export const createDashboard = (
|
||||
hass: HomeAssistant,
|
||||
values: LovelaceDashboardCreateParams
|
||||
) =>
|
||||
hass.callWS<LovelaceDashboard>({
|
||||
type: "lovelace/dashboards/create",
|
||||
...values,
|
||||
});
|
||||
|
||||
export const updateDashboard = (
|
||||
hass: HomeAssistant,
|
||||
id: string,
|
||||
updates: Partial<LovelaceDashboardMutableParams>
|
||||
) =>
|
||||
hass.callWS<LovelaceDashboard>({
|
||||
type: "lovelace/dashboards/update",
|
||||
dashboard_id: id,
|
||||
...updates,
|
||||
});
|
||||
|
||||
export const deleteDashboard = (hass: HomeAssistant, id: string) =>
|
||||
hass.callWS({
|
||||
type: "lovelace/dashboards/delete",
|
||||
dashboard_id: id,
|
||||
});
|
||||
|
||||
export const fetchConfig = (
|
||||
conn: Connection,
|
||||
urlPath: string | null,
|
||||
force: boolean
|
||||
): Promise<LovelaceConfig> =>
|
||||
conn.sendMessagePromise({
|
||||
type: "lovelace/config",
|
||||
url_path: urlPath,
|
||||
force,
|
||||
});
|
||||
|
||||
export const saveConfig = (
|
||||
hass: HomeAssistant,
|
||||
urlPath: string | null,
|
||||
config: LovelaceConfig
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/save",
|
||||
url_path: urlPath,
|
||||
config,
|
||||
});
|
||||
|
||||
export const deleteConfig = (
|
||||
hass: HomeAssistant,
|
||||
urlPath: string | null
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/delete",
|
||||
url_path: urlPath,
|
||||
});
|
||||
|
||||
export const subscribeLovelaceUpdates = (
|
||||
conn: Connection,
|
||||
urlPath: string | null,
|
||||
@@ -67,7 +324,7 @@ export const getLovelaceCollection = (
|
||||
const fetchLegacyConfig = (
|
||||
conn: Connection,
|
||||
force: boolean
|
||||
): Promise<LegacyLovelaceConfig> =>
|
||||
): Promise<LovelaceConfig> =>
|
||||
conn.sendMessagePromise({
|
||||
type: "lovelace/config",
|
||||
force,
|
||||
@@ -90,3 +347,15 @@ export const getLegacyLovelaceCollection = (conn: Connection) =>
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
export interface ActionHandlerOptions {
|
||||
hasHold?: boolean;
|
||||
hasDoubleClick?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ActionHandlerDetail {
|
||||
action: "hold" | "tap" | "double_tap";
|
||||
}
|
||||
|
||||
export type ActionHandlerEvent = HASSDomEvent<ActionHandlerDetail>;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { HASSDomEvent } from "../../common/dom/fire_event";
|
||||
|
||||
export interface ActionHandlerOptions {
|
||||
hasHold?: boolean;
|
||||
hasDoubleClick?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ActionHandlerDetail {
|
||||
action: "hold" | "tap" | "double_tap";
|
||||
}
|
||||
|
||||
export type ActionHandlerEvent = HASSDomEvent<ActionHandlerDetail>;
|
||||
@@ -1,67 +0,0 @@
|
||||
import type { HassServiceTarget } from "home-assistant-js-websocket";
|
||||
|
||||
export interface ToggleActionConfig extends BaseActionConfig {
|
||||
action: "toggle";
|
||||
}
|
||||
|
||||
export interface CallServiceActionConfig extends BaseActionConfig {
|
||||
action: "call-service";
|
||||
service: string;
|
||||
target?: HassServiceTarget;
|
||||
// "service_data" is kept for backwards compatibility. Replaced by "data".
|
||||
service_data?: Record<string, unknown>;
|
||||
data?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface NavigateActionConfig extends BaseActionConfig {
|
||||
action: "navigate";
|
||||
navigation_path: string;
|
||||
navigation_replace?: boolean;
|
||||
}
|
||||
|
||||
export interface UrlActionConfig extends BaseActionConfig {
|
||||
action: "url";
|
||||
url_path: string;
|
||||
}
|
||||
|
||||
export interface MoreInfoActionConfig extends BaseActionConfig {
|
||||
action: "more-info";
|
||||
}
|
||||
|
||||
export interface AssistActionConfig extends BaseActionConfig {
|
||||
action: "assist";
|
||||
pipeline_id?: string;
|
||||
start_listening?: boolean;
|
||||
}
|
||||
|
||||
export interface NoActionConfig extends BaseActionConfig {
|
||||
action: "none";
|
||||
}
|
||||
|
||||
export interface CustomActionConfig extends BaseActionConfig {
|
||||
action: "fire-dom-event";
|
||||
}
|
||||
|
||||
export interface BaseActionConfig {
|
||||
action: string;
|
||||
confirmation?: ConfirmationRestrictionConfig;
|
||||
}
|
||||
|
||||
export interface ConfirmationRestrictionConfig {
|
||||
text?: string;
|
||||
exemptions?: RestrictionConfig[];
|
||||
}
|
||||
|
||||
export interface RestrictionConfig {
|
||||
user: string;
|
||||
}
|
||||
|
||||
export type ActionConfig =
|
||||
| ToggleActionConfig
|
||||
| CallServiceActionConfig
|
||||
| NavigateActionConfig
|
||||
| UrlActionConfig
|
||||
| MoreInfoActionConfig
|
||||
| AssistActionConfig
|
||||
| NoActionConfig
|
||||
| CustomActionConfig;
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface LovelaceBadgeConfig {
|
||||
type?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export interface LovelaceCardConfig {
|
||||
index?: number;
|
||||
view_index?: number;
|
||||
view_layout?: any;
|
||||
type: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface LovelaceStrategyConfig {
|
||||
type: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import type { Connection } from "home-assistant-js-websocket";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { LovelaceResource } from "../resource";
|
||||
import type { LovelaceStrategyConfig } from "./strategy";
|
||||
import type { LovelaceViewRawConfig } from "./view";
|
||||
|
||||
export interface LovelaceDashboardBaseConfig {}
|
||||
|
||||
export interface LovelaceConfig extends LovelaceDashboardBaseConfig {
|
||||
title?: string;
|
||||
background?: string;
|
||||
views: LovelaceViewRawConfig[];
|
||||
}
|
||||
|
||||
export interface LovelaceDashboardStrategyConfig
|
||||
extends LovelaceDashboardBaseConfig {
|
||||
strategy: LovelaceStrategyConfig;
|
||||
}
|
||||
|
||||
export interface LegacyLovelaceConfig extends LovelaceConfig {
|
||||
resources?: LovelaceResource[];
|
||||
}
|
||||
|
||||
export type LovelaceRawConfig =
|
||||
| LovelaceConfig
|
||||
| LovelaceDashboardStrategyConfig;
|
||||
|
||||
export function isStrategyDashboard(
|
||||
config: LovelaceRawConfig
|
||||
): config is LovelaceDashboardStrategyConfig {
|
||||
return "strategy" in config;
|
||||
}
|
||||
|
||||
export const fetchConfig = (
|
||||
conn: Connection,
|
||||
urlPath: string | null,
|
||||
force: boolean
|
||||
): Promise<LovelaceRawConfig> =>
|
||||
conn.sendMessagePromise({
|
||||
type: "lovelace/config",
|
||||
url_path: urlPath,
|
||||
force,
|
||||
});
|
||||
|
||||
export const saveConfig = (
|
||||
hass: HomeAssistant,
|
||||
urlPath: string | null,
|
||||
config: LovelaceRawConfig
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/save",
|
||||
url_path: urlPath,
|
||||
config,
|
||||
});
|
||||
|
||||
export const deleteConfig = (
|
||||
hass: HomeAssistant,
|
||||
urlPath: string | null
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/config/delete",
|
||||
url_path: urlPath,
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
import type { LovelaceBadgeConfig } from "./badge";
|
||||
import type { LovelaceCardConfig } from "./card";
|
||||
import type { LovelaceStrategyConfig } from "./strategy";
|
||||
|
||||
export interface ShowViewConfig {
|
||||
user?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceBaseViewConfig {
|
||||
index?: number;
|
||||
title?: string;
|
||||
path?: string;
|
||||
icon?: string;
|
||||
theme?: string;
|
||||
panel?: boolean;
|
||||
background?: string;
|
||||
visible?: boolean | ShowViewConfig[];
|
||||
subview?: boolean;
|
||||
back_path?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceViewConfig extends LovelaceBaseViewConfig {
|
||||
type?: string;
|
||||
badges?: Array<string | LovelaceBadgeConfig>;
|
||||
cards?: LovelaceCardConfig[];
|
||||
}
|
||||
|
||||
export interface LovelaceStrategyViewConfig extends LovelaceBaseViewConfig {
|
||||
strategy: LovelaceStrategyConfig;
|
||||
}
|
||||
|
||||
export type LovelaceViewRawConfig =
|
||||
| LovelaceViewConfig
|
||||
| LovelaceStrategyViewConfig;
|
||||
|
||||
export function isStrategyView(
|
||||
view: LovelaceViewRawConfig
|
||||
): view is LovelaceStrategyViewConfig {
|
||||
return "strategy" in view;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import type { HomeAssistant } from "../../types";
|
||||
|
||||
export type LovelaceDashboard =
|
||||
| LovelaceYamlDashboard
|
||||
| LovelaceStorageDashboard;
|
||||
|
||||
interface LovelaceGenericDashboard {
|
||||
id: string;
|
||||
url_path: string;
|
||||
require_admin: boolean;
|
||||
show_in_sidebar: boolean;
|
||||
icon?: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface LovelaceYamlDashboard extends LovelaceGenericDashboard {
|
||||
mode: "yaml";
|
||||
filename: string;
|
||||
}
|
||||
|
||||
export interface LovelaceStorageDashboard extends LovelaceGenericDashboard {
|
||||
mode: "storage";
|
||||
}
|
||||
|
||||
export interface LovelaceDashboardMutableParams {
|
||||
require_admin: boolean;
|
||||
show_in_sidebar: boolean;
|
||||
icon?: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface LovelaceDashboardCreateParams
|
||||
extends LovelaceDashboardMutableParams {
|
||||
url_path: string;
|
||||
mode: "storage";
|
||||
}
|
||||
|
||||
export const fetchDashboards = (
|
||||
hass: HomeAssistant
|
||||
): Promise<LovelaceDashboard[]> =>
|
||||
hass.callWS({
|
||||
type: "lovelace/dashboards/list",
|
||||
});
|
||||
|
||||
export const createDashboard = (
|
||||
hass: HomeAssistant,
|
||||
values: LovelaceDashboardCreateParams
|
||||
) =>
|
||||
hass.callWS<LovelaceDashboard>({
|
||||
type: "lovelace/dashboards/create",
|
||||
...values,
|
||||
});
|
||||
|
||||
export const updateDashboard = (
|
||||
hass: HomeAssistant,
|
||||
id: string,
|
||||
updates: Partial<LovelaceDashboardMutableParams>
|
||||
) =>
|
||||
hass.callWS<LovelaceDashboard>({
|
||||
type: "lovelace/dashboards/update",
|
||||
dashboard_id: id,
|
||||
...updates,
|
||||
});
|
||||
|
||||
export const deleteDashboard = (hass: HomeAssistant, id: string) =>
|
||||
hass.callWS({
|
||||
type: "lovelace/dashboards/delete",
|
||||
dashboard_id: id,
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
import type { Connection } from "home-assistant-js-websocket";
|
||||
import type { HomeAssistant } from "../../types";
|
||||
|
||||
export type LovelaceResource = {
|
||||
id: string;
|
||||
type: "css" | "js" | "module" | "html";
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type LovelaceResourcesMutableParams = {
|
||||
res_type: LovelaceResource["type"];
|
||||
url: string;
|
||||
};
|
||||
|
||||
export const fetchResources = (conn: Connection): Promise<LovelaceResource[]> =>
|
||||
conn.sendMessagePromise({
|
||||
type: "lovelace/resources",
|
||||
});
|
||||
|
||||
export const createResource = (
|
||||
hass: HomeAssistant,
|
||||
values: LovelaceResourcesMutableParams
|
||||
) =>
|
||||
hass.callWS<LovelaceResource>({
|
||||
type: "lovelace/resources/create",
|
||||
...values,
|
||||
});
|
||||
|
||||
export const updateResource = (
|
||||
hass: HomeAssistant,
|
||||
id: string,
|
||||
updates: Partial<LovelaceResourcesMutableParams>
|
||||
) =>
|
||||
hass.callWS<LovelaceResource>({
|
||||
type: "lovelace/resources/update",
|
||||
resource_id: id,
|
||||
...updates,
|
||||
});
|
||||
|
||||
export const deleteResource = (hass: HomeAssistant, id: string) =>
|
||||
hass.callWS({
|
||||
type: "lovelace/resources/delete",
|
||||
resource_id: id,
|
||||
});
|
||||
@@ -1,9 +1,8 @@
|
||||
import { LovelaceRawConfig } from "./lovelace/config/types";
|
||||
import { LovelaceResource } from "./lovelace/resource";
|
||||
import { LovelaceConfig, LovelaceResource } from "./lovelace";
|
||||
import { RecorderInfo } from "./recorder";
|
||||
|
||||
export interface WindowWithPreloads extends Window {
|
||||
llConfProm?: Promise<LovelaceRawConfig>;
|
||||
llConfProm?: Promise<LovelaceConfig>;
|
||||
llResProm?: Promise<LovelaceResource[]>;
|
||||
recorderInfoProm?: Promise<RecorderInfo>;
|
||||
}
|
||||
|
||||
+9
-10
@@ -49,11 +49,11 @@ export type Selector =
|
||||
| TemplateSelector
|
||||
| ThemeSelector
|
||||
| TimeSelector
|
||||
| TriggerSelector
|
||||
| TTSSelector
|
||||
| TTSVoiceSelector
|
||||
| UiActionSelector
|
||||
| UiColorSelector;
|
||||
| UiColorSelector
|
||||
| UserSelector;
|
||||
|
||||
export interface ActionSelector {
|
||||
action: {
|
||||
@@ -374,13 +374,6 @@ export interface TimeSelector {
|
||||
time: {} | null;
|
||||
}
|
||||
|
||||
export interface TriggerSelector {
|
||||
trigger: {
|
||||
reorder_mode?: boolean;
|
||||
nested?: boolean;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export interface TTSSelector {
|
||||
tts: { language?: string } | null;
|
||||
}
|
||||
@@ -392,7 +385,6 @@ export interface TTSVoiceSelector {
|
||||
export interface UiActionSelector {
|
||||
ui_action: {
|
||||
actions?: UiAction[];
|
||||
default_action?: UiAction;
|
||||
} | null;
|
||||
}
|
||||
|
||||
@@ -401,6 +393,13 @@ export interface UiColorSelector {
|
||||
ui_color: {} | null;
|
||||
}
|
||||
|
||||
export interface UserSelector {
|
||||
user: {
|
||||
multiple?: boolean;
|
||||
include_system?: boolean;
|
||||
} | null;
|
||||
}
|
||||
|
||||
export const expandAreaTarget = (
|
||||
hass: HomeAssistant,
|
||||
areaId: string,
|
||||
|
||||
+5
-10
@@ -61,12 +61,7 @@ export const updateItem = (
|
||||
entity_id: string,
|
||||
item: TodoItem
|
||||
): Promise<ServiceCallResponse> =>
|
||||
hass.callService(
|
||||
"todo",
|
||||
"update_item",
|
||||
{ item: item.uid, rename: item.summary, status: item.status },
|
||||
{ entity_id }
|
||||
);
|
||||
hass.callService("todo", "update_item", item, { entity_id });
|
||||
|
||||
export const createItem = (
|
||||
hass: HomeAssistant,
|
||||
@@ -75,9 +70,9 @@ export const createItem = (
|
||||
): Promise<ServiceCallResponse> =>
|
||||
hass.callService(
|
||||
"todo",
|
||||
"add_item",
|
||||
"create_item",
|
||||
{
|
||||
item: summary,
|
||||
summary,
|
||||
},
|
||||
{ entity_id }
|
||||
);
|
||||
@@ -89,9 +84,9 @@ export const deleteItem = (
|
||||
): Promise<ServiceCallResponse> =>
|
||||
hass.callService(
|
||||
"todo",
|
||||
"remove_item",
|
||||
"delete_item",
|
||||
{
|
||||
item: uid,
|
||||
uid,
|
||||
},
|
||||
{ entity_id }
|
||||
);
|
||||
|
||||
@@ -61,7 +61,6 @@ export type TranslationCategory =
|
||||
| "state"
|
||||
| "entity"
|
||||
| "entity_component"
|
||||
| "exceptions"
|
||||
| "config"
|
||||
| "config_panel"
|
||||
| "options"
|
||||
|
||||
@@ -415,7 +415,6 @@ export class HaMoreInfoClimateTemperature extends LitElement {
|
||||
line-height: 64px;
|
||||
letter-spacing: -0.25px;
|
||||
margin: 0;
|
||||
direction: ltr;
|
||||
}
|
||||
.temperature span {
|
||||
display: inline-flex;
|
||||
|
||||
@@ -6,7 +6,6 @@ import { stateColorCss } from "../../../../common/entity/state_color";
|
||||
import "../../../../components/ha-control-slider";
|
||||
import { CoverEntity } from "../../../../data/cover";
|
||||
import { UNAVAILABLE } from "../../../../data/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
||||
@customElement("ha-more-info-cover-position")
|
||||
@@ -61,8 +60,6 @@ export class HaMoreInfoCoverPosition extends LitElement {
|
||||
"--control-slider-background": color,
|
||||
})}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_position}
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
</ha-control-slider>
|
||||
`;
|
||||
@@ -79,7 +76,6 @@ export class HaMoreInfoCoverPosition extends LitElement {
|
||||
--control-slider-color: var(--primary-color);
|
||||
--control-slider-background: var(--disabled-color);
|
||||
--control-slider-background-opacity: 0.2;
|
||||
--control-slider-tooltip-font-size: 20px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import { stateColorCss } from "../../../../common/entity/state_color";
|
||||
import "../../../../components/ha-control-slider";
|
||||
import { CoverEntity } from "../../../../data/cover";
|
||||
import { UNAVAILABLE } from "../../../../data/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
|
||||
export function generateTiltSliderTrackBackgroundGradient() {
|
||||
@@ -97,8 +96,6 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
|
||||
"--control-slider-background": color,
|
||||
})}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_tilt_position}
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
<div slot="background" class="gradient"></div>
|
||||
</ha-control-slider>
|
||||
@@ -116,7 +113,6 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
|
||||
--control-slider-color: var(--primary-color);
|
||||
--control-slider-background: var(--disabled-color);
|
||||
--control-slider-background-opacity: 0.2;
|
||||
--control-slider-tooltip-font-size: 20px;
|
||||
}
|
||||
.gradient {
|
||||
background: -webkit-linear-gradient(top, ${GRADIENT});
|
||||
|
||||
@@ -8,7 +8,6 @@ import "../../../../components/ha-control-select";
|
||||
import type { ControlSelectOption } from "../../../../components/ha-control-select";
|
||||
import "../../../../components/ha-control-slider";
|
||||
import { UNAVAILABLE } from "../../../../data/entity";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
|
||||
import {
|
||||
computeFanSpeedCount,
|
||||
computeFanSpeedIcon,
|
||||
@@ -131,8 +130,6 @@ export class HaMoreInfoFanSpeed extends LitElement {
|
||||
"--control-slider-background": color,
|
||||
})}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.fan.percentage}
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
</ha-control-slider>
|
||||
`;
|
||||
@@ -149,7 +146,6 @@ export class HaMoreInfoFanSpeed extends LitElement {
|
||||
--control-slider-color: var(--primary-color);
|
||||
--control-slider-background: var(--disabled-color);
|
||||
--control-slider-background-opacity: 0.2;
|
||||
--control-slider-tooltip-font-size: 20px;
|
||||
}
|
||||
ha-control-select {
|
||||
height: 45vh;
|
||||
|
||||
@@ -77,8 +77,6 @@ export class HaMoreInfoLightBrightness extends LitElement {
|
||||
"--control-slider-background": color,
|
||||
})}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
unit="%"
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
</ha-control-slider>
|
||||
`;
|
||||
@@ -95,7 +93,6 @@ export class HaMoreInfoLightBrightness extends LitElement {
|
||||
--control-slider-color: var(--primary-color);
|
||||
--control-slider-background: var(--disabled-color);
|
||||
--control-slider-background-opacity: 0.2;
|
||||
--control-slider-tooltip-font-size: 20px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
LightEntity,
|
||||
} from "../../../../data/light";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
@@ -94,8 +93,6 @@ class LightColorTempPicker extends LitElement {
|
||||
"--gradient": gradient,
|
||||
})}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
.unit=${DOMAIN_ATTRIBUTES_UNITS.light.color_temp_kelvin}
|
||||
.locale=${this.hass.locale}
|
||||
>
|
||||
</ha-control-slider>
|
||||
`;
|
||||
@@ -196,7 +193,6 @@ class LightColorTempPicker extends LitElement {
|
||||
top,
|
||||
var(--gradient)
|
||||
);
|
||||
--control-slider-tooltip-font-size: 20px;
|
||||
--control-slider-background-opacity: 1;
|
||||
}
|
||||
`,
|
||||
|
||||
@@ -434,7 +434,6 @@ class MoreInfoClimate extends LitElement {
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
direction: ltr;
|
||||
}
|
||||
ha-select {
|
||||
width: 100%;
|
||||
|
||||
@@ -33,12 +33,36 @@ class MoreInfoCover extends LitElement {
|
||||
|
||||
@property({ attribute: false }) public stateObj?: CoverEntity;
|
||||
|
||||
@state() private _livePosition?: number;
|
||||
|
||||
@state() private _liveTilt?: number;
|
||||
|
||||
@state() private _mode?: Mode;
|
||||
|
||||
private _setMode(ev) {
|
||||
this._mode = ev.currentTarget.mode;
|
||||
}
|
||||
|
||||
private _positionSliderMoved(ev) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
this._livePosition = value;
|
||||
}
|
||||
|
||||
private _positionValueChanged() {
|
||||
this._livePosition = undefined;
|
||||
}
|
||||
|
||||
private _tiltSliderMoved(ev) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
this._liveTilt = value;
|
||||
}
|
||||
|
||||
private _tiltValueChanged() {
|
||||
this._liveTilt = undefined;
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues): void {
|
||||
super.willUpdate(changedProps);
|
||||
if (changedProps.has("stateObj") && this.stateObj) {
|
||||
@@ -53,11 +77,20 @@ class MoreInfoCover extends LitElement {
|
||||
}
|
||||
|
||||
private get _stateOverride() {
|
||||
const stateDisplay = this.hass.formatEntityState(this.stateObj!);
|
||||
const liveValue = this._livePosition ?? this._liveTilt;
|
||||
|
||||
const forcedState =
|
||||
liveValue != null ? (liveValue ? "open" : "closed") : undefined;
|
||||
|
||||
const stateDisplay = this.hass.formatEntityState(
|
||||
this.stateObj!,
|
||||
forcedState
|
||||
);
|
||||
|
||||
const positionStateDisplay = computeCoverPositionStateDisplay(
|
||||
this.stateObj!,
|
||||
this.hass
|
||||
this.hass,
|
||||
liveValue
|
||||
);
|
||||
|
||||
if (positionStateDisplay) {
|
||||
@@ -114,6 +147,8 @@ class MoreInfoCover extends LitElement {
|
||||
<ha-more-info-cover-position
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
@slider-moved=${this._positionSliderMoved}
|
||||
@value-changed=${this._positionValueChanged}
|
||||
></ha-more-info-cover-position>
|
||||
`
|
||||
: nothing}
|
||||
@@ -122,6 +157,8 @@ class MoreInfoCover extends LitElement {
|
||||
<ha-more-info-cover-tilt-position
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
@slider-moved=${this._tiltSliderMoved}
|
||||
@value-changed=${this._tiltValueChanged}
|
||||
></ha-more-info-cover-tilt-position>
|
||||
`
|
||||
: nothing}
|
||||
|
||||
@@ -40,6 +40,18 @@ class MoreInfoFan extends LitElement {
|
||||
|
||||
@state() public _presetMode?: string;
|
||||
|
||||
@state() private _liveSpeed?: number;
|
||||
|
||||
private _speedSliderMoved(ev) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
this._liveSpeed = value;
|
||||
}
|
||||
|
||||
private _speedValueChanged() {
|
||||
this._liveSpeed = undefined;
|
||||
}
|
||||
|
||||
private _toggle = () => {
|
||||
const service = this.stateObj?.state === "on" ? "turn_off" : "turn_on";
|
||||
forwardHaptic("light");
|
||||
@@ -92,14 +104,23 @@ class MoreInfoFan extends LitElement {
|
||||
}
|
||||
|
||||
private get _stateOverride() {
|
||||
const stateDisplay = this.hass.formatEntityState(this.stateObj!);
|
||||
const liveValue = this._liveSpeed;
|
||||
|
||||
const forcedState =
|
||||
liveValue != null ? (liveValue ? "on" : "off") : undefined;
|
||||
|
||||
const stateDisplay = this.hass.formatEntityState(
|
||||
this.stateObj!,
|
||||
forcedState
|
||||
);
|
||||
|
||||
const positionStateDisplay = computeFanSpeedStateDisplay(
|
||||
this.stateObj!,
|
||||
this.hass
|
||||
this.hass,
|
||||
liveValue
|
||||
);
|
||||
|
||||
if (positionStateDisplay && stateActive(this.stateObj!)) {
|
||||
if (positionStateDisplay && (stateActive(this.stateObj!) || liveValue)) {
|
||||
return positionStateDisplay;
|
||||
}
|
||||
return stateDisplay;
|
||||
@@ -144,6 +165,8 @@ class MoreInfoFan extends LitElement {
|
||||
<ha-more-info-fan-speed
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
@slider-moved=${this._speedSliderMoved}
|
||||
@value-changed=${this._speedValueChanged}
|
||||
>
|
||||
</ha-more-info-fan-speed>
|
||||
`
|
||||
|
||||
@@ -250,7 +250,6 @@ class MoreInfoHumidifier extends LitElement {
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
direction: ltr;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -60,10 +60,29 @@ class MoreInfoLight extends LitElement {
|
||||
|
||||
@state() private _effect?: string;
|
||||
|
||||
@state() private _selectedBrightness?: number;
|
||||
|
||||
@state() private _colorTempPreview?: number;
|
||||
|
||||
@state() private _mainControl: MainControl = "brightness";
|
||||
|
||||
private _brightnessChanged(ev) {
|
||||
const value = (ev.detail as any).value;
|
||||
if (isNaN(value)) return;
|
||||
this._selectedBrightness = (value * 255) / 100;
|
||||
}
|
||||
|
||||
private _tempColorHovered(ev: CustomEvent<HASSDomEvents["color-hovered"]>) {
|
||||
if (ev.detail && "color_temp_kelvin" in ev.detail) {
|
||||
this._colorTempPreview = ev.detail.color_temp_kelvin;
|
||||
} else {
|
||||
this._colorTempPreview = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues<typeof this>): void {
|
||||
if (changedProps.has("stateObj")) {
|
||||
this._selectedBrightness = this.stateObj?.attributes.brightness;
|
||||
this._effect = this.stateObj?.attributes.effect;
|
||||
}
|
||||
}
|
||||
@@ -79,8 +98,19 @@ class MoreInfoLight extends LitElement {
|
||||
}
|
||||
|
||||
private get _stateOverride() {
|
||||
if (this.stateObj?.attributes.brightness) {
|
||||
return this.hass.formatEntityAttributeValue(this.stateObj!, "brightness");
|
||||
if (this._colorTempPreview) {
|
||||
return this.hass.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"color_temp_kelvin",
|
||||
this._colorTempPreview
|
||||
);
|
||||
}
|
||||
if (this._selectedBrightness) {
|
||||
return this.hass.formatEntityAttributeValue(
|
||||
this.stateObj!,
|
||||
"brightness",
|
||||
this._selectedBrightness
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -138,6 +168,7 @@ class MoreInfoLight extends LitElement {
|
||||
<ha-more-info-light-brightness
|
||||
.stateObj=${this.stateObj}
|
||||
.hass=${this.hass}
|
||||
@slider-moved=${this._brightnessChanged}
|
||||
>
|
||||
</ha-more-info-light-brightness>
|
||||
`
|
||||
@@ -156,6 +187,7 @@ class MoreInfoLight extends LitElement {
|
||||
<light-color-temp-picker
|
||||
.hass=${this.hass}
|
||||
.stateObj=${this.stateObj}
|
||||
@color-hovered=${this._tempColorHovered}
|
||||
>
|
||||
</light-color-temp-picker>
|
||||
`
|
||||
|
||||
@@ -243,7 +243,6 @@ class MoreInfoWaterHeater extends LitElement {
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
line-height: 28px;
|
||||
direction: ltr;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -356,7 +356,6 @@ class MoreInfoWeather extends LitElement {
|
||||
.templow {
|
||||
min-width: 48px;
|
||||
text-align: right;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.templow {
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
fuzzyFilterSort,
|
||||
} from "../../common/string/filter/sequence-matching";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
import "../../components/ha-label";
|
||||
import "../../components/ha-chip";
|
||||
import "../../components/ha-circular-progress";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-list-item";
|
||||
@@ -326,17 +326,19 @@ export class QuickBar extends LitElement {
|
||||
hasMeta
|
||||
>
|
||||
<span>
|
||||
<ha-label
|
||||
<ha-chip
|
||||
.label=${item.categoryText}
|
||||
hasIcon
|
||||
class="command-category ${item.categoryKey}"
|
||||
>
|
||||
${item.iconPath
|
||||
? html`
|
||||
<ha-svg-icon .path=${item.iconPath} slot="icon"></ha-svg-icon>
|
||||
`
|
||||
: nothing}
|
||||
${item.categoryText}
|
||||
</ha-label>
|
||||
? html`<ha-svg-icon
|
||||
.path=${item.iconPath}
|
||||
slot="icon"
|
||||
></ha-svg-icon>`
|
||||
: ""}
|
||||
${item.categoryText}</ha-chip
|
||||
>
|
||||
</span>
|
||||
|
||||
<span class="command-text">${item.primaryText}</span>
|
||||
@@ -764,7 +766,6 @@ export class QuickBar extends LitElement {
|
||||
haStyleDialog,
|
||||
css`
|
||||
mwc-list {
|
||||
position: relative;
|
||||
--mdc-list-vertical-padding: 0;
|
||||
}
|
||||
.heading {
|
||||
@@ -814,20 +815,20 @@ export class QuickBar extends LitElement {
|
||||
}
|
||||
|
||||
.command-category {
|
||||
--ha-label-icon-color: #585858;
|
||||
--ha-label-text-color: #212121;
|
||||
--ha-chip-icon-color: #585858;
|
||||
--ha-chip-text-color: #212121;
|
||||
}
|
||||
|
||||
.command-category.reload {
|
||||
--ha-label-background-color: #cddc39;
|
||||
--ha-chip-background-color: #cddc39;
|
||||
}
|
||||
|
||||
.command-category.navigation {
|
||||
--ha-label-background-color: var(--light-primary-color);
|
||||
--ha-chip-background-color: var(--light-primary-color);
|
||||
}
|
||||
|
||||
.command-category.server_control {
|
||||
--ha-label-background-color: var(--warning-color);
|
||||
--ha-chip-background-color: var(--warning-color);
|
||||
}
|
||||
|
||||
span.command-text {
|
||||
|
||||
@@ -15,8 +15,7 @@ import { hassUrl } from "../data/auth";
|
||||
import { isExternal } from "../data/external";
|
||||
import { getRecorderInfo } from "../data/recorder";
|
||||
import { subscribeFrontendUserData } from "../data/frontend";
|
||||
import { fetchConfig } from "../data/lovelace/config/types";
|
||||
import { fetchResources } from "../data/lovelace/resource";
|
||||
import { fetchConfig, fetchResources } from "../data/lovelace";
|
||||
import { subscribePanels } from "../data/ws-panels";
|
||||
import { subscribeThemes } from "../data/ws-themes";
|
||||
import { subscribeRepairsIssueRegistry } from "../data/repairs";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -50,9 +50,19 @@
|
||||
<body>
|
||||
<div id="ha-launch-screen">
|
||||
<div class="ha-launch-screen-spacer"></div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
|
||||
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
|
||||
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
|
||||
<svg
|
||||
viewBox="0 0 240 240"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M240 224.762C240 233.012 233.25 239.762 225 239.762H15C6.75 239.762 0 233.012 0 224.762V134.762C0 126.512 4.77 114.993 10.61 109.153L109.39 10.3725C115.22 4.5425 124.77 4.5425 130.6 10.3725L229.39 109.162C235.22 114.992 240 126.522 240 134.772V224.772V224.762Z"
|
||||
fill="#F2F4F9"
|
||||
/>
|
||||
<path
|
||||
d="M229.39 109.153L130.61 10.3725C124.78 4.5425 115.23 4.5425 109.4 10.3725L10.61 109.153C4.78 114.983 0 126.512 0 134.762V224.762C0 233.012 6.75 239.762 15 239.762H107.27L66.64 199.132C64.55 199.852 62.32 200.262 60 200.262C48.7 200.262 39.5 191.062 39.5 179.762C39.5 168.462 48.7 159.262 60 159.262C71.3 159.262 80.5 168.462 80.5 179.762C80.5 182.092 80.09 184.322 79.37 186.412L111 218.042V102.162C104.2 98.8225 99.5 91.8425 99.5 83.7725C99.5 72.4725 108.7 63.2725 120 63.2725C131.3 63.2725 140.5 72.4725 140.5 83.7725C140.5 91.8425 135.8 98.8225 129 102.162V183.432L160.46 151.972C159.84 150.012 159.5 147.932 159.5 145.772C159.5 134.472 168.7 125.272 180 125.272C191.3 125.272 200.5 134.472 200.5 145.772C200.5 157.072 191.3 166.272 180 166.272C177.5 166.272 175.12 165.802 172.91 164.982L129 208.892V239.772H225C233.25 239.772 240 233.022 240 224.772V134.772C240 126.522 235.23 115.002 229.39 109.162V109.153Z"
|
||||
fill="#18BCF2"
|
||||
/>
|
||||
</svg>
|
||||
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
|
||||
</div>
|
||||
|
||||
@@ -584,10 +584,6 @@ class DialogCalendarEventEditor extends LitElement {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: min(600px, 95vw);
|
||||
--mdc-dialog-max-width: min(600px, 95vw);
|
||||
}
|
||||
state-info {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { SelectedDetail } from "@material/mwc-list";
|
||||
import { formatInTimeZone, toDate } from "date-fns-tz";
|
||||
import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
||||
import { css, html, LitElement, PropertyValues, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import type { Options, WeekdayStr } from "rrule";
|
||||
import { ByWeekday, RRule, Weekday } from "rrule";
|
||||
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
|
||||
import { stopPropagation } from "../../common/dom/stop_propagation";
|
||||
import { LocalizeKeys } from "../../common/translations/localize";
|
||||
import "../../components/chips/ha-chip-set";
|
||||
import "../../components/chips/ha-filter-chip";
|
||||
import "../../components/ha-chip";
|
||||
import "../../components/ha-date-input";
|
||||
import "../../components/ha-list-item";
|
||||
import "../../components/ha-select";
|
||||
@@ -16,17 +16,17 @@ import type { HaSelect } from "../../components/ha-select";
|
||||
import "../../components/ha-textfield";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import {
|
||||
DEFAULT_COUNT,
|
||||
MonthlyRepeatItem,
|
||||
RepeatEnd,
|
||||
RepeatFrequency,
|
||||
convertFrequency,
|
||||
convertRepeatFrequency,
|
||||
DEFAULT_COUNT,
|
||||
getMonthdayRepeatFromRule,
|
||||
getMonthlyRepeatItems,
|
||||
getMonthlyRepeatWeekdayFromRule,
|
||||
getWeekday,
|
||||
getWeekdays,
|
||||
MonthlyRepeatItem,
|
||||
RepeatEnd,
|
||||
RepeatFrequency,
|
||||
ruleByWeekDay,
|
||||
untilValue,
|
||||
} from "./recurrence";
|
||||
@@ -240,24 +240,22 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
renderWeekly() {
|
||||
return html`
|
||||
${this.renderInterval()}
|
||||
<ha-chip-set class="weekdays">
|
||||
<div class="weekdays">
|
||||
${this._allWeekdays!.map(
|
||||
(item) => html`
|
||||
<ha-filter-chip
|
||||
no-leading-icon
|
||||
<ha-chip
|
||||
.value=${item}
|
||||
.selected=${this._weekday.has(item)}
|
||||
class=${classMap({ active: this._weekday.has(item) })}
|
||||
@click=${this._onWeekdayToggle}
|
||||
.label=${this.hass.localize(
|
||||
>${this.hass.localize(
|
||||
`ui.components.calendar.event.repeat.weekly.weekday.${
|
||||
item.toLowerCase() as Lowercase<WeekdayStr>
|
||||
}`
|
||||
)}
|
||||
)}</ha-chip
|
||||
>
|
||||
</ha-filter-chip>
|
||||
`
|
||||
)}
|
||||
</ha-chip-set>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -381,10 +379,10 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
private _onWeekdayToggle(e: MouseEvent) {
|
||||
const target = e.currentTarget as any;
|
||||
const value = target.value as WeekdayStr;
|
||||
if (this._weekday.has(value)) {
|
||||
this._weekday.delete(value);
|
||||
} else {
|
||||
if (!target.classList.contains("active")) {
|
||||
this._weekday.add(value);
|
||||
} else {
|
||||
this._weekday.delete(value);
|
||||
}
|
||||
this.requestUpdate("_weekday");
|
||||
}
|
||||
@@ -506,6 +504,8 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.weekdays {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
ha-textfield:last-child,
|
||||
@@ -513,6 +513,11 @@ export class RecurrenceRuleEditor extends LitElement {
|
||||
.weekdays:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.active {
|
||||
--ha-chip-background-color: var(--primary-color);
|
||||
--ha-chip-text-color: var(--text-primary-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -342,7 +342,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
||||
|
||||
private _duplicateOption(ev) {
|
||||
const index = (ev.target as any).idx;
|
||||
this._createOption(deepClone(ensureArray(this.action.choose)[index]));
|
||||
this._addOption(deepClone(ensureArray(this.action.choose)[index]));
|
||||
}
|
||||
|
||||
protected firstUpdated() {
|
||||
@@ -399,15 +399,11 @@ export class HaChooseAction extends LitElement implements ActionElement {
|
||||
});
|
||||
}
|
||||
|
||||
private _addOption() {
|
||||
this._createOption({ conditions: [], sequence: [] });
|
||||
}
|
||||
|
||||
private _createOption(opt: ChooseActionChoice) {
|
||||
private _addOption(opt?: ChooseActionChoice) {
|
||||
const choose = this.action.choose
|
||||
? [...ensureArray(this.action.choose)]
|
||||
: [];
|
||||
choose.push(opt);
|
||||
choose.push(opt ?? { conditions: [], sequence: [] });
|
||||
fireEvent(this, "value-changed", {
|
||||
value: { ...this.action, choose },
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ import type {
|
||||
RowClickedEvent,
|
||||
} from "../../../components/data-table/ha-data-table";
|
||||
import "../../../components/ha-button-related-filter-menu";
|
||||
import "../../../components/ha-label";
|
||||
import "../../../components/ha-chip";
|
||||
import "../../../components/ha-fab";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-icon-overflow-menu";
|
||||
@@ -202,11 +202,11 @@ class HaAutomationPicker extends LitElement {
|
||||
template: (automation) =>
|
||||
automation.disabled
|
||||
? html`
|
||||
<ha-label>
|
||||
<ha-chip>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.automation.picker.disabled"
|
||||
)}
|
||||
</ha-label>
|
||||
</ha-chip>
|
||||
`
|
||||
: "",
|
||||
};
|
||||
|
||||
@@ -8,17 +8,16 @@ import {
|
||||
} from "@mdi/js";
|
||||
import { HassEntities, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import "../../../components/chips/ha-assist-chip";
|
||||
import "../../../components/ha-button-menu";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
@@ -35,9 +34,9 @@ import {
|
||||
subscribeRepairsIssueRegistry,
|
||||
} from "../../../data/repairs";
|
||||
import {
|
||||
UpdateEntity,
|
||||
checkForEntityUpdates,
|
||||
filterUpdateEntitiesWithInstall,
|
||||
UpdateEntity,
|
||||
} from "../../../data/update";
|
||||
import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar";
|
||||
import { showRestartDialog } from "../../../dialogs/restart/show-dialog-restart";
|
||||
@@ -232,17 +231,15 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
></ha-config-repairs>
|
||||
${totalRepairIssues > repairsIssues.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/repairs"
|
||||
.label=${this.hass.localize(
|
||||
<a class="button" href="/config/repairs">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.repairs.more_repairs",
|
||||
{
|
||||
count:
|
||||
totalRepairIssues - repairsIssues.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
@@ -260,17 +257,15 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
></ha-config-updates>
|
||||
${totalUpdates > canInstallUpdates.length
|
||||
? html`
|
||||
<ha-assist-chip
|
||||
href="/config/updates"
|
||||
label=${this.hass.localize(
|
||||
<a class="button" href="/config/updates">
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.updates.more_updates",
|
||||
{
|
||||
count:
|
||||
totalUpdates - canInstallUpdates.length,
|
||||
}
|
||||
)}
|
||||
>
|
||||
</ha-assist-chip>
|
||||
</a>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
@@ -354,8 +349,13 @@ class HaConfigDashboard extends SubscribeMixin(LitElement) {
|
||||
text-decoration: none;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
ha-assist-chip {
|
||||
a.button {
|
||||
display: inline-block;
|
||||
color: var(--primary-text-color);
|
||||
padding: 6px 16px;
|
||||
margin: 8px 16px 16px 16px;
|
||||
border-radius: 32px;
|
||||
border: 1px solid var(--divider-color);
|
||||
}
|
||||
.title {
|
||||
font-size: 16px;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user