mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 13:07:49 +00:00
Merge branch 'dev' into break-out-assist-chat
This commit is contained in:
commit
adf77e1e80
@ -1,16 +1,7 @@
|
|||||||
diff --git a/modular/sortable.core.esm.js b/modular/sortable.core.esm.js
|
diff --git a/modular/sortable.core.esm.js b/modular/sortable.core.esm.js
|
||||||
index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b441f523f 100644
|
index 8b5e49b011713c8859c669069fbe85ce53974e1d..6a0afc92787157b8a31c38cc5f67dfa526090a00 100644
|
||||||
--- a/modular/sortable.core.esm.js
|
--- a/modular/sortable.core.esm.js
|
||||||
+++ b/modular/sortable.core.esm.js
|
+++ b/modular/sortable.core.esm.js
|
||||||
@@ -1461,7 +1461,7 @@ Sortable.prototype = /** @lends Sortable.prototype */{
|
|
||||||
}
|
|
||||||
target = parent; // store last element
|
|
||||||
}
|
|
||||||
- /* jshint boss:true */ while (parent = parent.parentNode);
|
|
||||||
+ /* jshint boss:true */ while (parent = parent.parentNode || parent.getRootNode().host);
|
|
||||||
}
|
|
||||||
_unhideGhostForTarget();
|
|
||||||
}
|
|
||||||
@@ -1781,11 +1781,16 @@ Sortable.prototype = /** @lends Sortable.prototype */{
|
@@ -1781,11 +1781,16 @@ Sortable.prototype = /** @lends Sortable.prototype */{
|
||||||
}
|
}
|
||||||
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) {
|
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) {
|
||||||
@ -33,7 +24,7 @@ index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b
|
|||||||
}
|
}
|
||||||
parentEl = el; // actualization
|
parentEl = el; // actualization
|
||||||
|
|
||||||
@@ -1802,7 +1807,13 @@ Sortable.prototype = /** @lends Sortable.prototype */{
|
@@ -1802,7 +1807,12 @@ Sortable.prototype = /** @lends Sortable.prototype */{
|
||||||
targetRect = getRect(target);
|
targetRect = getRect(target);
|
||||||
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, false) !== false) {
|
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, false) !== false) {
|
||||||
capture();
|
capture();
|
||||||
@ -44,11 +35,10 @@ index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b
|
|||||||
+ catch(err) {
|
+ catch(err) {
|
||||||
+ return completed(false);
|
+ return completed(false);
|
||||||
+ }
|
+ }
|
||||||
+
|
|
||||||
parentEl = el; // actualization
|
parentEl = el; // actualization
|
||||||
|
|
||||||
changed();
|
changed();
|
||||||
@@ -1849,12 +1860,17 @@ Sortable.prototype = /** @lends Sortable.prototype */{
|
@@ -1849,10 +1859,15 @@ Sortable.prototype = /** @lends Sortable.prototype */{
|
||||||
_silent = true;
|
_silent = true;
|
||||||
setTimeout(_unsilent, 30);
|
setTimeout(_unsilent, 30);
|
||||||
capture();
|
capture();
|
||||||
@ -56,8 +46,6 @@ index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b
|
|||||||
- el.appendChild(dragEl);
|
- el.appendChild(dragEl);
|
||||||
- } else {
|
- } else {
|
||||||
- target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
|
- target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
|
||||||
- }
|
|
||||||
|
|
||||||
+ try {
|
+ try {
|
||||||
+ if (after && !nextSibling) {
|
+ if (after && !nextSibling) {
|
||||||
+ el.appendChild(dragEl);
|
+ el.appendChild(dragEl);
|
||||||
@ -67,7 +55,6 @@ index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b
|
|||||||
+ }
|
+ }
|
||||||
+ catch(err) {
|
+ catch(err) {
|
||||||
+ return completed(false);
|
+ return completed(false);
|
||||||
+ }
|
}
|
||||||
|
|
||||||
// Undo chrome's scroll adjustment (has no effect on other browsers)
|
// Undo chrome's scroll adjustment (has no effect on other browsers)
|
||||||
if (scrolledPastTop) {
|
|
||||||
scrollBy(scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop);
|
|
File diff suppressed because one or more lines are too long
@ -6,4 +6,4 @@ enableGlobalCache: false
|
|||||||
|
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
|
||||||
yarnPath: .yarn/releases/yarn-4.4.1.cjs
|
yarnPath: .yarn/releases/yarn-4.5.0.cjs
|
||||||
|
@ -15,23 +15,29 @@ const brotliOptions = {
|
|||||||
};
|
};
|
||||||
const zopfliOptions = { threshold: 150 };
|
const zopfliOptions = { threshold: 150 };
|
||||||
|
|
||||||
const compressDistBrotli = (rootDir, modernDir) =>
|
const compressDistBrotli = (rootDir, modernDir, compressServiceWorker = true) =>
|
||||||
gulp
|
gulp
|
||||||
.src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], {
|
.src(
|
||||||
|
[
|
||||||
|
`${modernDir}/**/${filesGlob}`,
|
||||||
|
compressServiceWorker ? `${rootDir}/sw-modern.js` : undefined,
|
||||||
|
].filter(Boolean),
|
||||||
|
{
|
||||||
base: rootDir,
|
base: rootDir,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
.pipe(brotli(brotliOptions))
|
.pipe(brotli(brotliOptions))
|
||||||
.pipe(gulp.dest(rootDir));
|
.pipe(gulp.dest(rootDir));
|
||||||
|
|
||||||
const compressDistZopfli = (rootDir, modernDir) =>
|
const compressDistZopfli = (rootDir, modernDir, compressModern = false) =>
|
||||||
gulp
|
gulp
|
||||||
.src(
|
.src(
|
||||||
[
|
[
|
||||||
`${rootDir}/**/${filesGlob}`,
|
`${rootDir}/**/${filesGlob}`,
|
||||||
`!${modernDir}/**/${filesGlob}`,
|
compressModern ? undefined : `!${modernDir}/**/${filesGlob}`,
|
||||||
`!${rootDir}/{sw-modern,service_worker}.js`,
|
`!${rootDir}/{sw-modern,service_worker}.js`,
|
||||||
`${rootDir}/{authorize,onboarding}.html`,
|
`${rootDir}/{authorize,onboarding}.html`,
|
||||||
],
|
].filter(Boolean),
|
||||||
{ base: rootDir }
|
{ base: rootDir }
|
||||||
)
|
)
|
||||||
.pipe(zopfli(zopfliOptions))
|
.pipe(zopfli(zopfliOptions))
|
||||||
@ -40,12 +46,20 @@ const compressDistZopfli = (rootDir, modernDir) =>
|
|||||||
const compressAppBrotli = () =>
|
const compressAppBrotli = () =>
|
||||||
compressDistBrotli(paths.app_output_root, paths.app_output_latest);
|
compressDistBrotli(paths.app_output_root, paths.app_output_latest);
|
||||||
const compressHassioBrotli = () =>
|
const compressHassioBrotli = () =>
|
||||||
compressDistBrotli(paths.hassio_output_root, paths.hassio_output_latest);
|
compressDistBrotli(
|
||||||
|
paths.hassio_output_root,
|
||||||
|
paths.hassio_output_latest,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
const compressAppZopfli = () =>
|
const compressAppZopfli = () =>
|
||||||
compressDistZopfli(paths.app_output_root, paths.app_output_latest);
|
compressDistZopfli(paths.app_output_root, paths.app_output_latest);
|
||||||
const compressHassioZopfli = () =>
|
const compressHassioZopfli = () =>
|
||||||
compressDistZopfli(paths.hassio_output_root, paths.hassio_output_latest);
|
compressDistZopfli(
|
||||||
|
paths.hassio_output_root,
|
||||||
|
paths.hassio_output_latest,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
gulp.task("compress-app", gulp.parallel(compressAppBrotli, compressAppZopfli));
|
gulp.task("compress-app", gulp.parallel(compressAppBrotli, compressAppZopfli));
|
||||||
gulp.task(
|
gulp.task(
|
||||||
|
@ -60,6 +60,12 @@ function copyPolyfills(staticDir) {
|
|||||||
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js.map"),
|
npmPath("@webcomponents/webcomponentsjs/webcomponents-bundle.js.map"),
|
||||||
staticPath("polyfills/")
|
staticPath("polyfills/")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// dialog-polyfill css
|
||||||
|
copyFileDir(
|
||||||
|
npmPath("dialog-polyfill/dialog-polyfill.css"),
|
||||||
|
staticPath("polyfills/")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyLoaderJS(staticDir) {
|
function copyLoaderJS(staticDir) {
|
||||||
|
@ -111,9 +111,37 @@ export const demoEntitiesSections: DemoConfig["entities"] = (localize) =>
|
|||||||
friendly_name: "Living room Temperature",
|
friendly_name: "Living room Temperature",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"sensor.outdoor_temperature": {
|
||||||
|
entity_id: "sensor.outdoor_temperature",
|
||||||
|
state: "10.5",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
unit_of_measurement: "°C",
|
||||||
|
device_class: "temperature",
|
||||||
|
friendly_name: "Outdoor temperature",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.outdoor_humidity": {
|
||||||
|
entity_id: "sensor.outdoor_humidity",
|
||||||
|
state: "70.4",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
unit_of_measurement: "%",
|
||||||
|
device_class: "humidity",
|
||||||
|
friendly_name: "Outdoor humidity",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"device_tracker.car": {
|
||||||
|
entity_id: "sensor.outdoor_humidity",
|
||||||
|
state: "not_home",
|
||||||
|
attributes: {
|
||||||
|
friendly_name: "Car",
|
||||||
|
icon: "mdi:car",
|
||||||
|
},
|
||||||
|
},
|
||||||
"media_player.living_room_nest_mini": {
|
"media_player.living_room_nest_mini": {
|
||||||
entity_id: "media_player.living_room_nest_mini",
|
entity_id: "media_player.living_room_nest_mini",
|
||||||
state: "on",
|
state: "playing",
|
||||||
attributes: {
|
attributes: {
|
||||||
device_class: "speaker",
|
device_class: "speaker",
|
||||||
volume_level: 0.18,
|
volume_level: 0.18,
|
||||||
|
@ -9,6 +9,22 @@ export const demoLovelaceSections: DemoConfig["lovelace"] = (localize) => ({
|
|||||||
title: isFrontpageEmbed ? "Home Assistant" : "Demo",
|
title: isFrontpageEmbed ? "Home Assistant" : "Demo",
|
||||||
path: "home",
|
path: "home",
|
||||||
icon: "mdi:home-assistant",
|
icon: "mdi:home-assistant",
|
||||||
|
badges: [
|
||||||
|
{
|
||||||
|
type: "entity",
|
||||||
|
entity: "sensor.outdoor_temperature",
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "entity",
|
||||||
|
entity: "sensor.outdoor_humidity",
|
||||||
|
color: "indigo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "entity",
|
||||||
|
entity: "device_tracker.car",
|
||||||
|
},
|
||||||
|
],
|
||||||
sections: [
|
sections: [
|
||||||
...(isFrontpageEmbed
|
...(isFrontpageEmbed
|
||||||
? []
|
? []
|
||||||
|
@ -232,7 +232,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
],
|
],
|
||||||
action: [
|
action: [
|
||||||
{
|
{
|
||||||
service: "input_boolean.toggle",
|
action: "input_boolean.toggle",
|
||||||
target: {
|
target: {
|
||||||
entity_id: "input_boolean.toggle_4",
|
entity_id: "input_boolean.toggle_4",
|
||||||
},
|
},
|
||||||
@ -268,7 +268,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
],
|
],
|
||||||
default: [
|
default: [
|
||||||
{
|
{
|
||||||
service: "input_boolean.toggle",
|
action: "input_boolean.toggle",
|
||||||
alias: "Toggle 2",
|
alias: "Toggle 2",
|
||||||
target: {
|
target: {
|
||||||
entity_id: "input_boolean.toggle_2",
|
entity_id: "input_boolean.toggle_2",
|
||||||
@ -277,7 +277,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
service: "input_boolean.toggle",
|
action: "input_boolean.toggle",
|
||||||
target: {
|
target: {
|
||||||
entity_id: "input_boolean.toggle_4",
|
entity_id: "input_boolean.toggle_4",
|
||||||
},
|
},
|
||||||
|
@ -143,7 +143,7 @@ export const motionLightTrace: DemoTrace = {
|
|||||||
],
|
],
|
||||||
action: [
|
action: [
|
||||||
{
|
{
|
||||||
service: "light.turn_on",
|
action: "light.turn_on",
|
||||||
target: {
|
target: {
|
||||||
entity_id: "light.elgato_key_light_air",
|
entity_id: "light.elgato_key_light_air",
|
||||||
},
|
},
|
||||||
@ -162,7 +162,7 @@ export const motionLightTrace: DemoTrace = {
|
|||||||
delay: 0,
|
delay: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
service: "light.turn_off",
|
action: "light.turn_off",
|
||||||
target: {
|
target: {
|
||||||
entity_id: "light.elgato_key_light_air",
|
entity_id: "light.elgato_key_light_air",
|
||||||
},
|
},
|
||||||
|
@ -64,6 +64,7 @@ const DEVICES: DeviceRegistryEntry[] = [
|
|||||||
labels: [],
|
labels: [],
|
||||||
created_at: 0,
|
created_at: 0,
|
||||||
modified_at: 0,
|
modified_at: 0,
|
||||||
|
primary_config_entry: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: "backyard",
|
area_id: "backyard",
|
||||||
@ -86,6 +87,7 @@ const DEVICES: DeviceRegistryEntry[] = [
|
|||||||
labels: [],
|
labels: [],
|
||||||
created_at: 0,
|
created_at: 0,
|
||||||
modified_at: 0,
|
modified_at: 0,
|
||||||
|
primary_config_entry: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: null,
|
area_id: null,
|
||||||
@ -108,6 +110,7 @@ const DEVICES: DeviceRegistryEntry[] = [
|
|||||||
labels: [],
|
labels: [],
|
||||||
created_at: 0,
|
created_at: 0,
|
||||||
modified_at: 0,
|
modified_at: 0,
|
||||||
|
primary_config_entry: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ const DEVICES: DeviceRegistryEntry[] = [
|
|||||||
labels: [],
|
labels: [],
|
||||||
created_at: 0,
|
created_at: 0,
|
||||||
modified_at: 0,
|
modified_at: 0,
|
||||||
|
primary_config_entry: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: "backyard",
|
area_id: "backyard",
|
||||||
@ -86,6 +87,7 @@ const DEVICES: DeviceRegistryEntry[] = [
|
|||||||
labels: [],
|
labels: [],
|
||||||
created_at: 0,
|
created_at: 0,
|
||||||
modified_at: 0,
|
modified_at: 0,
|
||||||
|
primary_config_entry: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
area_id: null,
|
area_id: null,
|
||||||
@ -108,6 +110,7 @@ const DEVICES: DeviceRegistryEntry[] = [
|
|||||||
labels: [],
|
labels: [],
|
||||||
created_at: 0,
|
created_at: 0,
|
||||||
modified_at: 0,
|
modified_at: 0,
|
||||||
|
primary_config_entry: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -232,6 +232,7 @@ const createDeviceRegistryEntries = (
|
|||||||
labels: [],
|
labels: [],
|
||||||
created_at: 0,
|
created_at: 0,
|
||||||
modified_at: 0,
|
modified_at: 0,
|
||||||
|
primary_config_entry: null,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ import type { HomeAssistant } from "../../../../src/types";
|
|||||||
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
||||||
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
import type { HaTextField } from "../../../../src/components/ha-textfield";
|
||||||
import "../../../../src/components/ha-textfield";
|
import "../../../../src/components/ha-textfield";
|
||||||
import "../../../../src/components/ha-list-new";
|
import "../../../../src/components/ha-md-list";
|
||||||
import "../../../../src/components/ha-list-item-new";
|
import "../../../../src/components/ha-md-list-item";
|
||||||
|
|
||||||
@customElement("dialog-hassio-repositories")
|
@customElement("dialog-hassio-repositories")
|
||||||
class HassioRepositoriesDialog extends LitElement {
|
class HassioRepositoriesDialog extends LitElement {
|
||||||
@ -107,11 +107,11 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<ha-list-new>
|
<ha-md-list>
|
||||||
${repositories.length
|
${repositories.length
|
||||||
? repositories.map(
|
? repositories.map(
|
||||||
(repo) => html`
|
(repo) => html`
|
||||||
<ha-list-item-new class="option">
|
<ha-md-list-item class="option">
|
||||||
${repo.name}
|
${repo.name}
|
||||||
<div slot="supporting-text">
|
<div slot="supporting-text">
|
||||||
<div>${repo.maintainer}</div>
|
<div>${repo.maintainer}</div>
|
||||||
@ -142,11 +142,11 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</simple-tooltip>
|
</simple-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
: html`<ha-list-item-new> No repositories </ha-list-item-new>`}
|
: html`<ha-md-list-item> No repositories </ha-md-list-item>`}
|
||||||
</ha-list-new>
|
</ha-md-list>
|
||||||
<div class="layout horizontal bottom">
|
<div class="layout horizontal bottom">
|
||||||
<ha-textfield
|
<ha-textfield
|
||||||
class="flex-auto"
|
class="flex-auto"
|
||||||
@ -209,7 +209,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
div.delete ha-icon-button {
|
div.delete ha-icon-button {
|
||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
}
|
}
|
||||||
ha-list-item-new {
|
ha-md-list-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
47
package.json
47
package.json
@ -27,9 +27,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "7.25.6",
|
"@babel/runtime": "7.25.6",
|
||||||
"@braintree/sanitize-url": "7.1.0",
|
"@braintree/sanitize-url": "7.1.0",
|
||||||
"@codemirror/autocomplete": "6.18.0",
|
"@codemirror/autocomplete": "6.18.1",
|
||||||
"@codemirror/commands": "6.6.0",
|
"@codemirror/commands": "6.6.2",
|
||||||
"@codemirror/language": "6.10.2",
|
"@codemirror/language": "6.10.3",
|
||||||
"@codemirror/legacy-modes": "6.4.1",
|
"@codemirror/legacy-modes": "6.4.1",
|
||||||
"@codemirror/search": "6.5.6",
|
"@codemirror/search": "6.5.6",
|
||||||
"@codemirror/state": "6.4.1",
|
"@codemirror/state": "6.4.1",
|
||||||
@ -80,7 +80,7 @@
|
|||||||
"@material/mwc-top-app-bar": "0.27.0",
|
"@material/mwc-top-app-bar": "0.27.0",
|
||||||
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
"@material/mwc-top-app-bar-fixed": "0.27.0",
|
||||||
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
|
||||||
"@material/web": "2.1.0",
|
"@material/web": "2.2.0",
|
||||||
"@mdi/js": "7.4.47",
|
"@mdi/js": "7.4.47",
|
||||||
"@mdi/svg": "7.4.47",
|
"@mdi/svg": "7.4.47",
|
||||||
"@polymer/paper-item": "3.0.1",
|
"@polymer/paper-item": "3.0.1",
|
||||||
@ -88,8 +88,8 @@
|
|||||||
"@polymer/paper-tabs": "3.1.0",
|
"@polymer/paper-tabs": "3.1.0",
|
||||||
"@polymer/polymer": "3.5.1",
|
"@polymer/polymer": "3.5.1",
|
||||||
"@thomasloven/round-slider": "0.6.0",
|
"@thomasloven/round-slider": "0.6.0",
|
||||||
"@vaadin/combo-box": "24.4.7",
|
"@vaadin/combo-box": "24.4.9",
|
||||||
"@vaadin/vaadin-themable-mixin": "24.4.7",
|
"@vaadin/vaadin-themable-mixin": "24.4.9",
|
||||||
"@vibrant/color": "3.2.1-alpha.1",
|
"@vibrant/color": "3.2.1-alpha.1",
|
||||||
"@vibrant/core": "3.2.1-alpha.1",
|
"@vibrant/core": "3.2.1-alpha.1",
|
||||||
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
|
||||||
@ -102,10 +102,11 @@
|
|||||||
"comlink": "4.4.1",
|
"comlink": "4.4.1",
|
||||||
"core-js": "3.38.1",
|
"core-js": "3.38.1",
|
||||||
"cropperjs": "1.6.2",
|
"cropperjs": "1.6.2",
|
||||||
"date-fns": "3.6.0",
|
"date-fns": "4.1.0",
|
||||||
"date-fns-tz": "3.1.3",
|
"date-fns-tz": "3.1.3",
|
||||||
"deep-clone-simple": "1.1.1",
|
"deep-clone-simple": "1.1.1",
|
||||||
"deep-freeze": "0.0.1",
|
"deep-freeze": "0.0.1",
|
||||||
|
"dialog-polyfill": "0.5.6",
|
||||||
"element-internals-polyfill": "1.3.11",
|
"element-internals-polyfill": "1.3.11",
|
||||||
"fuse.js": "7.0.0",
|
"fuse.js": "7.0.0",
|
||||||
"google-timezones-json": "1.2.0",
|
"google-timezones-json": "1.2.0",
|
||||||
@ -118,7 +119,7 @@
|
|||||||
"leaflet-draw": "1.0.4",
|
"leaflet-draw": "1.0.4",
|
||||||
"lit": "2.8.0",
|
"lit": "2.8.0",
|
||||||
"luxon": "3.5.0",
|
"luxon": "3.5.0",
|
||||||
"marked": "14.1.0",
|
"marked": "14.1.2",
|
||||||
"memoize-one": "6.0.0",
|
"memoize-one": "6.0.0",
|
||||||
"node-vibrant": "3.2.1-alpha.1",
|
"node-vibrant": "3.2.1-alpha.1",
|
||||||
"proxy-polyfill": "0.3.2",
|
"proxy-polyfill": "0.3.2",
|
||||||
@ -127,13 +128,13 @@
|
|||||||
"qrcode": "1.5.4",
|
"qrcode": "1.5.4",
|
||||||
"roboto-fontface": "0.10.0",
|
"roboto-fontface": "0.10.0",
|
||||||
"rrule": "2.8.1",
|
"rrule": "2.8.1",
|
||||||
"sortablejs": "1.15.2",
|
"sortablejs": "1.15.3",
|
||||||
"stacktrace-js": "2.0.2",
|
"stacktrace-js": "2.0.2",
|
||||||
"superstruct": "2.0.2",
|
"superstruct": "2.0.2",
|
||||||
"tinykeys": "3.0.0",
|
"tinykeys": "3.0.0",
|
||||||
"tsparticles-engine": "2.12.0",
|
"tsparticles-engine": "2.12.0",
|
||||||
"tsparticles-preset-links": "2.12.0",
|
"tsparticles-preset-links": "2.12.0",
|
||||||
"ua-parser-js": "1.0.38",
|
"ua-parser-js": "1.0.39",
|
||||||
"unfetch": "5.0.0",
|
"unfetch": "5.0.0",
|
||||||
"vis-data": "7.1.9",
|
"vis-data": "7.1.9",
|
||||||
"vis-network": "9.1.9",
|
"vis-network": "9.1.9",
|
||||||
@ -155,7 +156,7 @@
|
|||||||
"@babel/plugin-transform-runtime": "7.25.4",
|
"@babel/plugin-transform-runtime": "7.25.4",
|
||||||
"@babel/preset-env": "7.25.4",
|
"@babel/preset-env": "7.25.4",
|
||||||
"@babel/preset-typescript": "7.24.7",
|
"@babel/preset-typescript": "7.24.7",
|
||||||
"@bundle-stats/plugin-webpack-filter": "4.15.0",
|
"@bundle-stats/plugin-webpack-filter": "4.15.1",
|
||||||
"@koa/cors": "5.0.0",
|
"@koa/cors": "5.0.0",
|
||||||
"@lokalise/node-api": "12.7.0",
|
"@lokalise/node-api": "12.7.0",
|
||||||
"@octokit/auth-oauth-device": "7.1.1",
|
"@octokit/auth-oauth-device": "7.1.1",
|
||||||
@ -189,7 +190,7 @@
|
|||||||
"@typescript-eslint/parser": "7.18.0",
|
"@typescript-eslint/parser": "7.18.0",
|
||||||
"@web/dev-server": "0.1.38",
|
"@web/dev-server": "0.1.38",
|
||||||
"@web/dev-server-rollup": "0.4.1",
|
"@web/dev-server-rollup": "0.4.1",
|
||||||
"babel-loader": "9.1.3",
|
"babel-loader": "9.2.1",
|
||||||
"babel-plugin-template-html-minifier": "4.1.0",
|
"babel-plugin-template-html-minifier": "4.1.0",
|
||||||
"browserslist-useragent-regexp": "4.1.3",
|
"browserslist-useragent-regexp": "4.1.3",
|
||||||
"chai": "5.1.1",
|
"chai": "5.1.1",
|
||||||
@ -198,11 +199,11 @@
|
|||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-airbnb-typescript": "18.0.0",
|
"eslint-config-airbnb-typescript": "18.0.0",
|
||||||
"eslint-config-prettier": "9.1.0",
|
"eslint-config-prettier": "9.1.0",
|
||||||
"eslint-import-resolver-webpack": "0.13.8",
|
"eslint-import-resolver-webpack": "0.13.9",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.30.0",
|
||||||
"eslint-plugin-lit": "1.14.0",
|
"eslint-plugin-lit": "1.15.0",
|
||||||
"eslint-plugin-lit-a11y": "4.1.4",
|
"eslint-plugin-lit-a11y": "4.1.4",
|
||||||
"eslint-plugin-unused-imports": "4.1.3",
|
"eslint-plugin-unused-imports": "4.1.4",
|
||||||
"eslint-plugin-wc": "2.1.1",
|
"eslint-plugin-wc": "2.1.1",
|
||||||
"fancy-log": "2.0.0",
|
"fancy-log": "2.0.0",
|
||||||
"fs-extra": "11.2.0",
|
"fs-extra": "11.2.0",
|
||||||
@ -213,10 +214,10 @@
|
|||||||
"gulp-rename": "2.0.0",
|
"gulp-rename": "2.0.0",
|
||||||
"gulp-zopfli-green": "6.0.2",
|
"gulp-zopfli-green": "6.0.2",
|
||||||
"html-minifier-terser": "7.2.0",
|
"html-minifier-terser": "7.2.0",
|
||||||
"husky": "9.1.5",
|
"husky": "9.1.6",
|
||||||
"instant-mocha": "1.5.2",
|
"instant-mocha": "1.5.2",
|
||||||
"jszip": "3.10.1",
|
"jszip": "3.10.1",
|
||||||
"lint-staged": "15.2.9",
|
"lint-staged": "15.2.10",
|
||||||
"lit-analyzer": "2.0.3",
|
"lit-analyzer": "2.0.3",
|
||||||
"lodash.merge": "4.6.2",
|
"lodash.merge": "4.6.2",
|
||||||
"lodash.template": "4.5.0",
|
"lodash.template": "4.5.0",
|
||||||
@ -232,16 +233,16 @@
|
|||||||
"rollup-plugin-terser": "7.0.2",
|
"rollup-plugin-terser": "7.0.2",
|
||||||
"rollup-plugin-visualizer": "5.12.0",
|
"rollup-plugin-visualizer": "5.12.0",
|
||||||
"serve-handler": "6.1.5",
|
"serve-handler": "6.1.5",
|
||||||
"sinon": "18.0.0",
|
"sinon": "19.0.2",
|
||||||
"systemjs": "6.15.1",
|
"systemjs": "6.15.1",
|
||||||
"tar": "7.4.3",
|
"tar": "7.4.3",
|
||||||
"terser-webpack-plugin": "5.3.10",
|
"terser-webpack-plugin": "5.3.10",
|
||||||
"transform-async-modules-webpack-plugin": "1.1.1",
|
"transform-async-modules-webpack-plugin": "1.1.1",
|
||||||
"ts-lit-plugin": "2.0.2",
|
"ts-lit-plugin": "2.0.2",
|
||||||
"typescript": "5.5.4",
|
"typescript": "5.6.2",
|
||||||
"webpack": "5.94.0",
|
"webpack": "5.94.0",
|
||||||
"webpack-cli": "5.1.4",
|
"webpack-cli": "5.1.4",
|
||||||
"webpack-dev-server": "5.0.4",
|
"webpack-dev-server": "5.1.0",
|
||||||
"webpack-manifest-plugin": "5.0.0",
|
"webpack-manifest-plugin": "5.0.0",
|
||||||
"webpack-stats-plugin": "1.1.3",
|
"webpack-stats-plugin": "1.1.3",
|
||||||
"webpackbar": "6.0.1",
|
"webpackbar": "6.0.1",
|
||||||
@ -255,8 +256,8 @@
|
|||||||
"clean-css": "5.3.3",
|
"clean-css": "5.3.3",
|
||||||
"@lit/reactive-element": "1.6.3",
|
"@lit/reactive-element": "1.6.3",
|
||||||
"@fullcalendar/daygrid": "6.1.15",
|
"@fullcalendar/daygrid": "6.1.15",
|
||||||
"sortablejs@1.15.2": "patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch",
|
"sortablejs@1.15.3": "patch:sortablejs@npm%3A1.15.3#~/.yarn/patches/sortablejs-npm-1.15.3-3235a8f83b.patch",
|
||||||
"leaflet-draw@1.0.4": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.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.4.1"
|
"packageManager": "yarn@4.5.0"
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20240903.1"
|
version = "20240909.1"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -234,7 +234,12 @@ export const SENSOR_ENTITIES = [
|
|||||||
"weather",
|
"weather",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ASSIST_ENTITIES = ["conversation", "stt", "tts"];
|
export const ASSIST_ENTITIES = [
|
||||||
|
"assist_satellite",
|
||||||
|
"conversation",
|
||||||
|
"stt",
|
||||||
|
"tts",
|
||||||
|
];
|
||||||
|
|
||||||
/** Domains that render an input element instead of a text value when displayed in a row.
|
/** Domains that render an input element instead of a text value when displayed in a row.
|
||||||
* Those rows should then not show a cursor pointer when hovered (which would normally
|
* Those rows should then not show a cursor pointer when hovered (which would normally
|
||||||
|
@ -25,7 +25,6 @@ import { fireEvent } from "../../common/dom/fire_event";
|
|||||||
import { stringCompare } from "../../common/string/compare";
|
import { stringCompare } from "../../common/string/compare";
|
||||||
import { debounce } from "../../common/util/debounce";
|
import { debounce } from "../../common/util/debounce";
|
||||||
import { groupBy } from "../../common/util/group-by";
|
import { groupBy } from "../../common/util/group-by";
|
||||||
import { nextRender } from "../../common/util/render-status";
|
|
||||||
import { haStyleScrollbar } from "../../resources/styles";
|
import { haStyleScrollbar } from "../../resources/styles";
|
||||||
import { loadVirtualizer } from "../../resources/virtualizer";
|
import { loadVirtualizer } from "../../resources/virtualizer";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
@ -35,6 +34,7 @@ import "../ha-svg-icon";
|
|||||||
import "../search-input";
|
import "../search-input";
|
||||||
import { filterData, sortData } from "./sort-filter";
|
import { filterData, sortData } from "./sort-filter";
|
||||||
import { LocalizeFunc } from "../../common/translations/localize";
|
import { LocalizeFunc } from "../../common/translations/localize";
|
||||||
|
import { nextRender } from "../../common/util/render-status";
|
||||||
|
|
||||||
export interface RowClickedEvent {
|
export interface RowClickedEvent {
|
||||||
id: string;
|
id: string;
|
||||||
@ -169,8 +169,6 @@ export class HaDataTable extends LitElement {
|
|||||||
|
|
||||||
@query("slot[name='header']") private _header!: HTMLSlotElement;
|
@query("slot[name='header']") private _header!: HTMLSlotElement;
|
||||||
|
|
||||||
@state() private _items: DataTableRowData[] = [];
|
|
||||||
|
|
||||||
@state() private _collapsedGroups: string[] = [];
|
@state() private _collapsedGroups: string[] = [];
|
||||||
|
|
||||||
private _checkableRowsCount?: number;
|
private _checkableRowsCount?: number;
|
||||||
@ -179,7 +177,9 @@ export class HaDataTable extends LitElement {
|
|||||||
|
|
||||||
private _sortColumns: SortableColumnContainer = {};
|
private _sortColumns: SortableColumnContainer = {};
|
||||||
|
|
||||||
private curRequest = 0;
|
private _curRequest = 0;
|
||||||
|
|
||||||
|
private _lastUpdate = 0;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@restoreScroll(".scroller") private _savedScrollPos?: number;
|
@restoreScroll(".scroller") private _savedScrollPos?: number;
|
||||||
@ -206,9 +206,9 @@ export class HaDataTable extends LitElement {
|
|||||||
|
|
||||||
public connectedCallback() {
|
public connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
if (this._items.length) {
|
if (this._filteredData.length) {
|
||||||
// Force update of location of rows
|
// Force update of location of rows
|
||||||
this._items = [...this._items];
|
this._filteredData = [...this._filteredData];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,16 +291,13 @@ export class HaDataTable extends LitElement {
|
|||||||
properties.has("columns") ||
|
properties.has("columns") ||
|
||||||
properties.has("_filter") ||
|
properties.has("_filter") ||
|
||||||
properties.has("sortColumn") ||
|
properties.has("sortColumn") ||
|
||||||
properties.has("sortDirection") ||
|
properties.has("sortDirection")
|
||||||
properties.has("groupColumn") ||
|
|
||||||
properties.has("groupOrder") ||
|
|
||||||
properties.has("_collapsedGroups")
|
|
||||||
) {
|
) {
|
||||||
this._sortFilterData();
|
this._sortFilterData();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.has("selectable") || properties.has("hiddenColumns")) {
|
if (properties.has("selectable") || properties.has("hiddenColumns")) {
|
||||||
this._items = [...this._items];
|
this._filteredData = [...this._filteredData];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,7 +464,15 @@ export class HaDataTable extends LitElement {
|
|||||||
scroller
|
scroller
|
||||||
class="mdc-data-table__content scroller ha-scrollbar"
|
class="mdc-data-table__content scroller ha-scrollbar"
|
||||||
@scroll=${this._saveScrollPos}
|
@scroll=${this._saveScrollPos}
|
||||||
.items=${this._items}
|
.items=${this._groupData(
|
||||||
|
this._filteredData,
|
||||||
|
localize,
|
||||||
|
this.appendRow,
|
||||||
|
this.hasFab,
|
||||||
|
this.groupColumn,
|
||||||
|
this.groupOrder,
|
||||||
|
this._collapsedGroups
|
||||||
|
)}
|
||||||
.keyFunction=${this._keyFunction}
|
.keyFunction=${this._keyFunction}
|
||||||
.renderItem=${renderRow}
|
.renderItem=${renderRow}
|
||||||
></lit-virtualizer>
|
></lit-virtualizer>
|
||||||
@ -602,8 +607,13 @@ export class HaDataTable extends LitElement {
|
|||||||
|
|
||||||
private async _sortFilterData() {
|
private async _sortFilterData() {
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
this.curRequest++;
|
const timeBetweenUpdate = startTime - this._lastUpdate;
|
||||||
const curRequest = this.curRequest;
|
const timeBetweenRequest = startTime - this._curRequest;
|
||||||
|
this._curRequest = startTime;
|
||||||
|
|
||||||
|
const forceUpdate =
|
||||||
|
!this._lastUpdate ||
|
||||||
|
(timeBetweenUpdate > 500 && timeBetweenRequest < 500);
|
||||||
|
|
||||||
let filteredData = this.data;
|
let filteredData = this.data;
|
||||||
if (this._filter) {
|
if (this._filter) {
|
||||||
@ -614,6 +624,10 @@ export class HaDataTable extends LitElement {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!forceUpdate && this._curRequest !== startTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const prom = this.sortColumn
|
const prom = this.sortColumn
|
||||||
? sortData(
|
? sortData(
|
||||||
filteredData,
|
filteredData,
|
||||||
@ -634,17 +648,30 @@ export class HaDataTable extends LitElement {
|
|||||||
setTimeout(resolve, 100 - elapsed);
|
setTimeout(resolve, 100 - elapsed);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (this.curRequest !== curRequest) {
|
|
||||||
|
if (!forceUpdate && this._curRequest !== startTime) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const localize = this.localizeFunc || this.hass.localize;
|
this._lastUpdate = startTime;
|
||||||
|
this._filteredData = data;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.appendRow || this.hasFab || this.groupColumn) {
|
private _groupData = memoizeOne(
|
||||||
|
(
|
||||||
|
data: DataTableRowData[],
|
||||||
|
localize: LocalizeFunc,
|
||||||
|
appendRow,
|
||||||
|
hasFab: boolean,
|
||||||
|
groupColumn: string | undefined,
|
||||||
|
groupOrder: string[] | undefined,
|
||||||
|
collapsedGroups: string[]
|
||||||
|
) => {
|
||||||
|
if (appendRow || hasFab || groupColumn) {
|
||||||
let items = [...data];
|
let items = [...data];
|
||||||
|
|
||||||
if (this.groupColumn) {
|
if (groupColumn) {
|
||||||
const grouped = groupBy(items, (item) => item[this.groupColumn!]);
|
const grouped = groupBy(items, (item) => item[groupColumn]);
|
||||||
if (grouped.undefined) {
|
if (grouped.undefined) {
|
||||||
// make sure ungrouped items are at the bottom
|
// make sure ungrouped items are at the bottom
|
||||||
grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
|
grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
|
||||||
@ -654,8 +681,8 @@ export class HaDataTable extends LitElement {
|
|||||||
[key: string]: DataTableRowData[];
|
[key: string]: DataTableRowData[];
|
||||||
} = Object.keys(grouped)
|
} = Object.keys(grouped)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
const orderA = this.groupOrder?.indexOf(a) ?? -1;
|
const orderA = groupOrder?.indexOf(a) ?? -1;
|
||||||
const orderB = this.groupOrder?.indexOf(b) ?? -1;
|
const orderB = groupOrder?.indexOf(b) ?? -1;
|
||||||
if (orderA !== orderB) {
|
if (orderA !== orderB) {
|
||||||
if (orderA === -1) {
|
if (orderA === -1) {
|
||||||
return 1;
|
return 1;
|
||||||
@ -687,7 +714,7 @@ export class HaDataTable extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
.path=${mdiChevronUp}
|
.path=${mdiChevronUp}
|
||||||
class=${this._collapsedGroups.includes(groupName)
|
class=${collapsedGroups.includes(groupName)
|
||||||
? "collapsed"
|
? "collapsed"
|
||||||
: ""}
|
: ""}
|
||||||
>
|
>
|
||||||
@ -697,27 +724,26 @@ export class HaDataTable extends LitElement {
|
|||||||
: groupName || ""}
|
: groupName || ""}
|
||||||
</div>`,
|
</div>`,
|
||||||
});
|
});
|
||||||
if (!this._collapsedGroups.includes(groupName)) {
|
if (!collapsedGroups.includes(groupName)) {
|
||||||
groupedItems.push(...rows);
|
groupedItems.push(...rows);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
items = groupedItems;
|
items = groupedItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.appendRow) {
|
if (appendRow) {
|
||||||
items.push({ append: true, content: this.appendRow });
|
items.push({ append: true, content: appendRow });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasFab) {
|
if (hasFab) {
|
||||||
items.push({ empty: true });
|
items.push({ empty: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
this._items = items;
|
return items;
|
||||||
} else {
|
|
||||||
this._items = data;
|
|
||||||
}
|
}
|
||||||
this._filteredData = data;
|
return data;
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
private _memFilterData = memoizeOne(
|
private _memFilterData = memoizeOne(
|
||||||
(
|
(
|
||||||
@ -802,8 +828,8 @@ export class HaDataTable extends LitElement {
|
|||||||
|
|
||||||
private _checkedRowsChanged() {
|
private _checkedRowsChanged() {
|
||||||
// force scroller to update, change it's items
|
// force scroller to update, change it's items
|
||||||
if (this._items.length) {
|
if (this._filteredData.length) {
|
||||||
this._items = [...this._items];
|
this._filteredData = [...this._filteredData];
|
||||||
}
|
}
|
||||||
fireEvent(this, "selection-changed", {
|
fireEvent(this, "selection-changed", {
|
||||||
value: this._checkedRows,
|
value: this._checkedRows,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { mdiTextureBox } from "@mdi/js";
|
import { mdiTextureBox } from "@mdi/js";
|
||||||
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||||
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { LitElement, PropertyValues, TemplateResult, html, nothing } from "lit";
|
import { LitElement, PropertyValues, TemplateResult, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
@ -20,12 +20,7 @@ import {
|
|||||||
getDeviceEntityDisplayLookup,
|
getDeviceEntityDisplayLookup,
|
||||||
} from "../data/device_registry";
|
} from "../data/device_registry";
|
||||||
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
|
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
|
||||||
import {
|
import { FloorRegistryEntry, getFloorAreaLookup } from "../data/floor_registry";
|
||||||
FloorRegistryEntry,
|
|
||||||
getFloorAreaLookup,
|
|
||||||
subscribeFloorRegistry,
|
|
||||||
} from "../data/floor_registry";
|
|
||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
|
||||||
import { HomeAssistant, ValueChangedEvent } from "../types";
|
import { HomeAssistant, ValueChangedEvent } from "../types";
|
||||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||||
import "./ha-combo-box";
|
import "./ha-combo-box";
|
||||||
@ -50,7 +45,7 @@ interface FloorAreaEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-area-floor-picker")
|
@customElement("ha-area-floor-picker")
|
||||||
export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
|
export class HaAreaFloorPicker extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
@ -111,22 +106,12 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public required = false;
|
@property({ type: Boolean }) public required = false;
|
||||||
|
|
||||||
@state() private _floors?: FloorRegistryEntry[];
|
|
||||||
|
|
||||||
@state() private _opened?: boolean;
|
@state() private _opened?: boolean;
|
||||||
|
|
||||||
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
||||||
|
|
||||||
private _init = false;
|
private _init = false;
|
||||||
|
|
||||||
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
|
||||||
return [
|
|
||||||
subscribeFloorRegistry(this.hass.connection, (floors) => {
|
|
||||||
this._floors = floors;
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public async open() {
|
public async open() {
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
await this.comboBox?.open();
|
await this.comboBox?.open();
|
||||||
@ -431,12 +416,12 @@ export class HaAreaFloorPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
protected updated(changedProps: PropertyValues) {
|
||||||
if (
|
if (
|
||||||
(!this._init && this.hass && this._floors) ||
|
(!this._init && this.hass) ||
|
||||||
(this._init && changedProps.has("_opened") && this._opened)
|
(this._init && changedProps.has("_opened") && this._opened)
|
||||||
) {
|
) {
|
||||||
this._init = true;
|
this._init = true;
|
||||||
const areas = this._getAreas(
|
const areas = this._getAreas(
|
||||||
this._floors!,
|
Object.values(this.hass.floors),
|
||||||
Object.values(this.hass.areas),
|
Object.values(this.hass.areas),
|
||||||
Object.values(this.hass.devices),
|
Object.values(this.hass.devices),
|
||||||
Object.values(this.hass.entities),
|
Object.values(this.hass.entities),
|
||||||
|
155
src/components/ha-badge.ts
Normal file
155
src/components/ha-badge.ts
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
import { classMap } from "lit/directives/class-map";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import "./ha-ripple";
|
||||||
|
|
||||||
|
type BadgeType = "badge" | "button";
|
||||||
|
|
||||||
|
@customElement("ha-badge")
|
||||||
|
export class HaBadge extends LitElement {
|
||||||
|
@property() public type: BadgeType = "badge";
|
||||||
|
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "icon-only" }) iconOnly = false;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const label = this.label;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="badge ${classMap({
|
||||||
|
"icon-only": this.iconOnly,
|
||||||
|
})}"
|
||||||
|
role=${ifDefined(this.type === "button" ? "button" : undefined)}
|
||||||
|
tabindex=${ifDefined(this.type === "button" ? "0" : undefined)}
|
||||||
|
>
|
||||||
|
<ha-ripple .disabled=${this.type !== "button"}></ha-ripple>
|
||||||
|
<slot name="icon"></slot>
|
||||||
|
${this.iconOnly
|
||||||
|
? nothing
|
||||||
|
: html`<span class="info">
|
||||||
|
${label ? html`<span class="label">${label}</span>` : nothing}
|
||||||
|
<span class="content"><slot></slot></span>
|
||||||
|
</span>`}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
--badge-color: var(--secondary-text-color);
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
.badge {
|
||||||
|
position: relative;
|
||||||
|
--ha-ripple-color: var(--badge-color);
|
||||||
|
--ha-ripple-hover-opacity: 0.04;
|
||||||
|
--ha-ripple-pressed-opacity: 0.12;
|
||||||
|
transition:
|
||||||
|
box-shadow 180ms ease-in-out,
|
||||||
|
border-color 180ms ease-in-out;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
height: var(--ha-badge-size, 36px);
|
||||||
|
min-width: var(--ha-badge-size, 36px);
|
||||||
|
padding: 0px 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: auto;
|
||||||
|
border-radius: var(
|
||||||
|
--ha-badge-border-radius,
|
||||||
|
calc(var(--ha-badge-size, 36px) / 2)
|
||||||
|
);
|
||||||
|
background: var(
|
||||||
|
--ha-card-background,
|
||||||
|
var(--card-background-color, white)
|
||||||
|
);
|
||||||
|
-webkit-backdrop-filter: var(--ha-card-backdrop-filter, none);
|
||||||
|
backdrop-filter: var(--ha-card-backdrop-filter, none);
|
||||||
|
border-width: var(--ha-card-border-width, 1px);
|
||||||
|
box-shadow: var(--ha-card-box-shadow, none);
|
||||||
|
border-style: solid;
|
||||||
|
border-color: var(
|
||||||
|
--ha-card-border-color,
|
||||||
|
var(--divider-color, #e0e0e0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.badge:focus-visible {
|
||||||
|
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
||||||
|
--shadow-focus: 0 0 0 1px var(--badge-color);
|
||||||
|
border-color: var(--badge-color);
|
||||||
|
box-shadow: var(--shadow-default), var(--shadow-focus);
|
||||||
|
}
|
||||||
|
[role="button"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
[role="button"]:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding-inline-start: initial;
|
||||||
|
text-align: center;
|
||||||
|
font-family: Roboto;
|
||||||
|
}
|
||||||
|
.label {
|
||||||
|
font-size: 10px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 10px;
|
||||||
|
letter-spacing: 0.1px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
font-size: 12px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 16px;
|
||||||
|
letter-spacing: 0.1px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
::slotted([slot="icon"]) {
|
||||||
|
--mdc-icon-size: 18px;
|
||||||
|
color: var(--badge-color);
|
||||||
|
line-height: 0;
|
||||||
|
margin-left: -4px;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-inline-start: -4px;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
}
|
||||||
|
::slotted(img[slot="icon"]) {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-left: -10px;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-inline-start: -10px;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
}
|
||||||
|
.badge.icon-only {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.badge.icon-only ::slotted([slot="icon"]) {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-inline-start: 0;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-badge": HaBadge;
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
import "@material/mwc-menu/mwc-menu-surface";
|
import "@material/mwc-menu/mwc-menu-surface";
|
||||||
import { mdiFilterVariantRemove, mdiTextureBox } from "@mdi/js";
|
import { mdiFilterVariantRemove, mdiTextureBox } from "@mdi/js";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
LitElement,
|
LitElement,
|
||||||
@ -15,13 +14,8 @@ import { repeat } from "lit/directives/repeat";
|
|||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { computeRTL } from "../common/util/compute_rtl";
|
import { computeRTL } from "../common/util/compute_rtl";
|
||||||
import {
|
import { getFloorAreaLookup } from "../data/floor_registry";
|
||||||
FloorRegistryEntry,
|
|
||||||
getFloorAreaLookup,
|
|
||||||
subscribeFloorRegistry,
|
|
||||||
} from "../data/floor_registry";
|
|
||||||
import { RelatedResult, findRelated } from "../data/search";
|
import { RelatedResult, findRelated } from "../data/search";
|
||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
|
||||||
import { haStyleScrollbar } from "../resources/styles";
|
import { haStyleScrollbar } from "../resources/styles";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./ha-check-list-item";
|
import "./ha-check-list-item";
|
||||||
@ -31,7 +25,7 @@ import "./ha-svg-icon";
|
|||||||
import "./ha-tree-indicator";
|
import "./ha-tree-indicator";
|
||||||
|
|
||||||
@customElement("ha-filter-floor-areas")
|
@customElement("ha-filter-floor-areas")
|
||||||
export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
|
export class HaFilterFloorAreas extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public value?: {
|
@property({ attribute: false }) public value?: {
|
||||||
@ -47,8 +41,6 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _shouldRender = false;
|
@state() private _shouldRender = false;
|
||||||
|
|
||||||
@state() private _floors?: FloorRegistryEntry[];
|
|
||||||
|
|
||||||
public willUpdate(properties: PropertyValues) {
|
public willUpdate(properties: PropertyValues) {
|
||||||
super.willUpdate(properties);
|
super.willUpdate(properties);
|
||||||
|
|
||||||
@ -60,7 +52,7 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const areas = this._areas(this.hass.areas, this._floors);
|
const areas = this._areas(this.hass.areas, this.hass.floors);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-expansion-panel
|
<ha-expansion-panel
|
||||||
@ -189,14 +181,6 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
|
|||||||
this._findRelated();
|
this._findRelated();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
|
||||||
return [
|
|
||||||
subscribeFloorRegistry(this.hass.connection, (floors) => {
|
|
||||||
this._floors = floors;
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changed) {
|
protected updated(changed) {
|
||||||
if (changed.has("expanded") && this.expanded) {
|
if (changed.has("expanded") && this.expanded) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -220,9 +204,9 @@ export class HaFilterFloorAreas extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _areas = memoizeOne(
|
private _areas = memoizeOne(
|
||||||
(areaReg: HomeAssistant["areas"], floors?: FloorRegistryEntry[]) => {
|
(areaReg: HomeAssistant["areas"], floorReg: HomeAssistant["floors"]) => {
|
||||||
const areas = Object.values(areaReg);
|
const areas = Object.values(areaReg);
|
||||||
|
const floors = Object.values(floorReg);
|
||||||
const floorAreaLookup = getFloorAreaLookup(areas);
|
const floorAreaLookup = getFloorAreaLookup(areas);
|
||||||
|
|
||||||
const unassisgnedAreas = areas.filter(
|
const unassisgnedAreas = areas.filter(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||||
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { LitElement, PropertyValues, TemplateResult, html } from "lit";
|
import { LitElement, PropertyValues, TemplateResult, html } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
@ -24,10 +24,8 @@ import {
|
|||||||
FloorRegistryEntry,
|
FloorRegistryEntry,
|
||||||
createFloorRegistryEntry,
|
createFloorRegistryEntry,
|
||||||
getFloorAreaLookup,
|
getFloorAreaLookup,
|
||||||
subscribeFloorRegistry,
|
|
||||||
} from "../data/floor_registry";
|
} from "../data/floor_registry";
|
||||||
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
|
||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
|
||||||
import { showFloorRegistryDetailDialog } from "../panels/config/areas/show-dialog-floor-registry-detail";
|
import { showFloorRegistryDetailDialog } from "../panels/config/areas/show-dialog-floor-registry-detail";
|
||||||
import { HomeAssistant, ValueChangedEvent } from "../types";
|
import { HomeAssistant, ValueChangedEvent } from "../types";
|
||||||
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
import type { HaDevicePickerDeviceFilterFunc } from "./device/ha-device-picker";
|
||||||
@ -53,7 +51,7 @@ const rowRenderer: ComboBoxLitRenderer<FloorRegistryEntry> = (item) =>
|
|||||||
</ha-list-item>`;
|
</ha-list-item>`;
|
||||||
|
|
||||||
@customElement("ha-floor-picker")
|
@customElement("ha-floor-picker")
|
||||||
export class HaFloorPicker extends SubscribeMixin(LitElement) {
|
export class HaFloorPicker extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
@ -111,8 +109,6 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _opened?: boolean;
|
@state() private _opened?: boolean;
|
||||||
|
|
||||||
@state() private _floors?: FloorRegistryEntry[];
|
|
||||||
|
|
||||||
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
||||||
|
|
||||||
private _suggestion?: string;
|
private _suggestion?: string;
|
||||||
@ -129,14 +125,6 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
|
|||||||
await this.comboBox?.focus();
|
await this.comboBox?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
|
||||||
return [
|
|
||||||
subscribeFloorRegistry(this.hass.connection, (floors) => {
|
|
||||||
this._floors = floors;
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getFloors = memoizeOne(
|
private _getFloors = memoizeOne(
|
||||||
(
|
(
|
||||||
floors: FloorRegistryEntry[],
|
floors: FloorRegistryEntry[],
|
||||||
@ -320,12 +308,12 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
protected updated(changedProps: PropertyValues) {
|
||||||
if (
|
if (
|
||||||
(!this._init && this.hass && this._floors) ||
|
(!this._init && this.hass) ||
|
||||||
(this._init && changedProps.has("_opened") && this._opened)
|
(this._init && changedProps.has("_opened") && this._opened)
|
||||||
) {
|
) {
|
||||||
this._init = true;
|
this._init = true;
|
||||||
const floors = this._getFloors(
|
const floors = this._getFloors(
|
||||||
this._floors!,
|
Object.values(this.hass.floors),
|
||||||
Object.values(this.hass.areas),
|
Object.values(this.hass.areas),
|
||||||
Object.values(this.hass.devices),
|
Object.values(this.hass.devices),
|
||||||
Object.values(this.hass.entities),
|
Object.values(this.hass.entities),
|
||||||
@ -360,8 +348,7 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
|
|||||||
? this.hass.localize("ui.components.floor-picker.floor")
|
? this.hass.localize("ui.components.floor-picker.floor")
|
||||||
: this.label}
|
: this.label}
|
||||||
.placeholder=${this.placeholder
|
.placeholder=${this.placeholder
|
||||||
? this._floors?.find((floor) => floor.floor_id === this.placeholder)
|
? this.hass.floors[this.placeholder]?.name
|
||||||
?.name
|
|
||||||
: undefined}
|
: undefined}
|
||||||
.renderer=${rowRenderer}
|
.renderer=${rowRenderer}
|
||||||
@filter-changed=${this._filterChanged}
|
@filter-changed=${this._filterChanged}
|
||||||
@ -460,7 +447,7 @@ export class HaFloorPicker extends SubscribeMixin(LitElement) {
|
|||||||
floor_id: floor.floor_id,
|
floor_id: floor.floor_id,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const floors = [...this._floors!, floor];
|
const floors = [...Object.values(this.hass.floors), floor];
|
||||||
this.comboBox.filteredItems = this._getFloors(
|
this.comboBox.filteredItems = this._getFloors(
|
||||||
floors,
|
floors,
|
||||||
Object.values(this.hass.areas)!,
|
Object.values(this.hass.areas)!,
|
||||||
|
@ -95,10 +95,10 @@ export const computeInitialHaFormData = (
|
|||||||
} else if (
|
} else if (
|
||||||
"action" in selector ||
|
"action" in selector ||
|
||||||
"trigger" in selector ||
|
"trigger" in selector ||
|
||||||
"condition" in selector ||
|
"condition" in selector
|
||||||
"media" in selector ||
|
|
||||||
"target" in selector
|
|
||||||
) {
|
) {
|
||||||
|
data[field.name] = [];
|
||||||
|
} else if ("media" in selector || "target" in selector) {
|
||||||
data[field.name] = {};
|
data[field.name] = {};
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -73,6 +73,10 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
schema: any
|
schema: any
|
||||||
) => string | undefined;
|
) => string | undefined;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public localizeValue?: (
|
||||||
|
key: string
|
||||||
|
) => string;
|
||||||
|
|
||||||
protected getFormProperties(): Record<string, any> {
|
protected getFormProperties(): Record<string, any> {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -145,6 +149,7 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
.disabled=${item.disabled || this.disabled || false}
|
.disabled=${item.disabled || this.disabled || false}
|
||||||
.placeholder=${item.required ? "" : item.default}
|
.placeholder=${item.required ? "" : item.default}
|
||||||
.helper=${this._computeHelper(item)}
|
.helper=${this._computeHelper(item)}
|
||||||
|
.localizeValue=${this.localizeValue}
|
||||||
.required=${item.required || false}
|
.required=${item.required || false}
|
||||||
.context=${this._generateContext(item)}
|
.context=${this._generateContext(item)}
|
||||||
></ha-selector>`
|
></ha-selector>`
|
||||||
|
@ -6,8 +6,8 @@ import type { HaIconButton } from "./ha-icon-button";
|
|||||||
import "./ha-menu";
|
import "./ha-menu";
|
||||||
import type { HaMenu } from "./ha-menu";
|
import type { HaMenu } from "./ha-menu";
|
||||||
|
|
||||||
@customElement("ha-button-menu-new")
|
@customElement("ha-md-button-menu")
|
||||||
export class HaButtonMenuNew extends LitElement {
|
export class HaMdButtonMenu extends LitElement {
|
||||||
protected readonly [FOCUS_TARGET];
|
protected readonly [FOCUS_TARGET];
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled = false;
|
@property({ type: Boolean }) public disabled = false;
|
||||||
@ -84,6 +84,6 @@ export class HaButtonMenuNew extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-button-menu-new": HaButtonMenuNew;
|
"ha-md-button-menu": HaMdButtonMenu;
|
||||||
}
|
}
|
||||||
}
|
}
|
210
src/components/ha-md-dialog.ts
Normal file
210
src/components/ha-md-dialog.ts
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
import { MdDialog } from "@material/web/dialog/dialog";
|
||||||
|
import {
|
||||||
|
type DialogAnimation,
|
||||||
|
DIALOG_DEFAULT_CLOSE_ANIMATION,
|
||||||
|
DIALOG_DEFAULT_OPEN_ANIMATION,
|
||||||
|
} from "@material/web/dialog/internal/animations";
|
||||||
|
import { css } from "lit";
|
||||||
|
import { customElement, property } from "lit/decorators";
|
||||||
|
|
||||||
|
let DIALOG_POLYFILL: Promise<typeof import("dialog-polyfill")>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Based on the home assistant design: https://design.home-assistant.io/#components/ha-dialogs
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@customElement("ha-md-dialog")
|
||||||
|
export class HaMdDialog extends MdDialog {
|
||||||
|
/**
|
||||||
|
* When true the dialog will not close when the user presses the esc key or press out of the dialog.
|
||||||
|
*/
|
||||||
|
@property({ attribute: "disable-cancel-action", type: Boolean })
|
||||||
|
public disableCancelAction = false;
|
||||||
|
|
||||||
|
private _polyfillDialogRegistered = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.addEventListener("cancel", this._handleCancel);
|
||||||
|
|
||||||
|
if (typeof HTMLDialogElement !== "function") {
|
||||||
|
this.addEventListener("open", this._handleOpen);
|
||||||
|
|
||||||
|
if (!DIALOG_POLYFILL) {
|
||||||
|
DIALOG_POLYFILL = import("dialog-polyfill");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if browser doesn't support animate API disable open/close animations
|
||||||
|
if (this.animate === undefined) {
|
||||||
|
this.quick = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent open in older browsers and wait for polyfill to load
|
||||||
|
private async _handleOpen(openEvent: Event) {
|
||||||
|
openEvent.preventDefault();
|
||||||
|
|
||||||
|
if (this._polyfillDialogRegistered) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._polyfillDialogRegistered = true;
|
||||||
|
this._loadPolyfillStylesheet("/static/polyfills/dialog-polyfill.css");
|
||||||
|
const dialog = this.shadowRoot?.querySelector(
|
||||||
|
"dialog"
|
||||||
|
) as HTMLDialogElement;
|
||||||
|
|
||||||
|
const dialogPolyfill = await DIALOG_POLYFILL;
|
||||||
|
dialogPolyfill.default.registerDialog(dialog);
|
||||||
|
this.removeEventListener("open", this._handleOpen);
|
||||||
|
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _loadPolyfillStylesheet(href) {
|
||||||
|
const link = document.createElement("link");
|
||||||
|
link.rel = "stylesheet";
|
||||||
|
link.href = href;
|
||||||
|
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
link.onload = () => resolve();
|
||||||
|
link.onerror = () =>
|
||||||
|
reject(new Error(`Stylesheet failed to load: ${href}`));
|
||||||
|
|
||||||
|
this.shadowRoot?.appendChild(link);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleCancel(closeEvent: Event) {
|
||||||
|
if (this.disableCancelAction) {
|
||||||
|
closeEvent.preventDefault();
|
||||||
|
const dialogElement = this.shadowRoot?.querySelector("dialog");
|
||||||
|
if (this.animate !== undefined) {
|
||||||
|
dialogElement?.animate(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
transform: "rotate(-1deg)",
|
||||||
|
"animation-timing-function": "ease-in",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: "rotate(1.5deg)",
|
||||||
|
"animation-timing-function": "ease-out",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
transform: "rotate(0deg)",
|
||||||
|
"animation-timing-function": "ease-in",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
duration: 200,
|
||||||
|
iterations: 2,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static override styles = [
|
||||||
|
...super.styles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
--md-dialog-container-color: var(--card-background-color);
|
||||||
|
--md-dialog-headline-color: var(--primary-text-color);
|
||||||
|
--md-dialog-supporting-text-color: var(--primary-text-color);
|
||||||
|
--md-sys-color-scrim: #000000;
|
||||||
|
|
||||||
|
--md-dialog-headline-weight: 400;
|
||||||
|
--md-dialog-headline-size: 1.574rem;
|
||||||
|
--md-dialog-supporting-text-size: 1rem;
|
||||||
|
--md-dialog-supporting-text-line-height: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([type="alert"]) {
|
||||||
|
max-width: 320px;
|
||||||
|
min-width: 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host(:not([type="alert"])) {
|
||||||
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
|
min-width: calc(
|
||||||
|
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
|
||||||
|
);
|
||||||
|
max-width: calc(
|
||||||
|
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
|
||||||
|
);
|
||||||
|
min-height: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
--md-dialog-container-shape: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:host ::slotted(ha-dialog-header) {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrim {
|
||||||
|
z-index: 10; // overlay navigation
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// by default the dialog open/close animation will be from/to the top
|
||||||
|
// but if we have a special mobile dialog which is at the bottom of the screen, an from bottom animation can be used:
|
||||||
|
const OPEN_FROM_BOTTOM_ANIMATION: DialogAnimation = {
|
||||||
|
...DIALOG_DEFAULT_OPEN_ANIMATION,
|
||||||
|
dialog: [
|
||||||
|
[
|
||||||
|
// Dialog slide up
|
||||||
|
[{ transform: "translateY(50px)" }, { transform: "translateY(0)" }],
|
||||||
|
{ duration: 500, easing: "cubic-bezier(.3,0,0,1)" },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
container: [
|
||||||
|
[
|
||||||
|
// Container fade in
|
||||||
|
[{ opacity: 0 }, { opacity: 1 }],
|
||||||
|
{ duration: 50, easing: "linear", pseudoElement: "::before" },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const CLOSE_TO_BOTTOM_ANIMATION: DialogAnimation = {
|
||||||
|
...DIALOG_DEFAULT_CLOSE_ANIMATION,
|
||||||
|
dialog: [
|
||||||
|
[
|
||||||
|
// Dialog slide down
|
||||||
|
[{ transform: "translateY(0)" }, { transform: "translateY(50px)" }],
|
||||||
|
{ duration: 150, easing: "cubic-bezier(.3,0,0,1)" },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
container: [
|
||||||
|
[
|
||||||
|
// Container fade out
|
||||||
|
[{ opacity: "1" }, { opacity: "0" }],
|
||||||
|
{ delay: 100, duration: 50, easing: "linear", pseudoElement: "::before" },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMobileOpenFromBottomAnimation = () => {
|
||||||
|
const matches = window.matchMedia(
|
||||||
|
"all and (max-width: 450px), all and (max-height: 500px)"
|
||||||
|
).matches;
|
||||||
|
return matches ? OPEN_FROM_BOTTOM_ANIMATION : DIALOG_DEFAULT_OPEN_ANIMATION;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMobileCloseToBottomAnimation = () => {
|
||||||
|
const matches = window.matchMedia(
|
||||||
|
"all and (max-width: 450px), all and (max-height: 500px)"
|
||||||
|
).matches;
|
||||||
|
return matches ? CLOSE_TO_BOTTOM_ANIMATION : DIALOG_DEFAULT_CLOSE_ANIMATION;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-md-dialog": HaMdDialog;
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,8 @@ import { MdListItem } from "@material/web/list/list-item";
|
|||||||
import { css } from "lit";
|
import { css } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
|
|
||||||
@customElement("ha-list-item-new")
|
@customElement("ha-md-list-item")
|
||||||
export class HaListItemNew extends MdListItem {
|
export class HaMdListItem extends MdListItem {
|
||||||
static override styles = [
|
static override styles = [
|
||||||
...super.styles,
|
...super.styles,
|
||||||
css`
|
css`
|
||||||
@ -21,6 +21,6 @@ export class HaListItemNew extends MdListItem {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-list-item-new": HaListItemNew;
|
"ha-md-list-item": HaMdListItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,8 +2,8 @@ import { MdList } from "@material/web/list/list";
|
|||||||
import { css } from "lit";
|
import { css } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
|
|
||||||
@customElement("ha-list-new")
|
@customElement("ha-md-list")
|
||||||
export class HaListNew extends MdList {
|
export class HaMdList extends MdList {
|
||||||
static override styles = [
|
static override styles = [
|
||||||
...super.styles,
|
...super.styles,
|
||||||
css`
|
css`
|
||||||
@ -16,6 +16,6 @@ export class HaListNew extends MdList {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-list-new": HaListNew;
|
"ha-md-list": HaMdList;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,8 +2,8 @@ import { MdMenuItem } from "@material/web/menu/menu-item";
|
|||||||
import { css } from "lit";
|
import { css } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
|
||||||
@customElement("ha-menu-item")
|
@customElement("ha-md-menu-item")
|
||||||
export class HaMenuItem extends MdMenuItem {
|
export class HaMdMenuItem extends MdMenuItem {
|
||||||
@property({ attribute: false }) clickAction?: (item?: HTMLElement) => void;
|
@property({ attribute: false }) clickAction?: (item?: HTMLElement) => void;
|
||||||
|
|
||||||
static override styles = [
|
static override styles = [
|
||||||
@ -41,6 +41,6 @@ export class HaMenuItem extends MdMenuItem {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-menu-item": HaMenuItem;
|
"ha-md-menu-item": HaMdMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ import {
|
|||||||
} from "@material/web/menu/internal/controllers/shared";
|
} from "@material/web/menu/internal/controllers/shared";
|
||||||
import { css } from "lit";
|
import { css } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import type { HaMenuItem } from "./ha-menu-item";
|
import type { HaMdMenuItem } from "./ha-md-menu-item";
|
||||||
|
|
||||||
@customElement("ha-menu")
|
@customElement("ha-menu")
|
||||||
export class HaMenu extends MdMenu {
|
export class HaMenu extends MdMenu {
|
||||||
@ -22,7 +22,7 @@ export class HaMenu extends MdMenu {
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
(ev.detail.initiator as HaMenuItem).clickAction?.(ev.detail.initiator);
|
(ev.detail.initiator as HaMdMenuItem).clickAction?.(ev.detail.initiator);
|
||||||
}
|
}
|
||||||
|
|
||||||
static override styles = [
|
static override styles = [
|
||||||
|
@ -31,7 +31,7 @@ export class HaColorRGBSelector extends LitElement {
|
|||||||
.label=${this.label || ""}
|
.label=${this.label || ""}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
.helper=${this.helper}
|
.helper=${this.helper}
|
||||||
.disalbled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@change=${this._valueChanged}
|
@change=${this._valueChanged}
|
||||||
></ha-textfield>
|
></ha-textfield>
|
||||||
`;
|
`;
|
||||||
|
@ -67,7 +67,9 @@ export class HaNumberSelector extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.label ? html`${this.label}${this.required ? "*" : ""}` : nothing}
|
${this.label && !isBox
|
||||||
|
? html`${this.label}${this.required ? "*" : ""}`
|
||||||
|
: nothing}
|
||||||
<div class="input">
|
<div class="input">
|
||||||
${!isBox
|
${!isBox
|
||||||
? html`
|
? html`
|
||||||
|
@ -240,12 +240,24 @@ export class HaServiceControl extends LitElement {
|
|||||||
...value,
|
...value,
|
||||||
selector: value.selector as Selector | undefined,
|
selector: value.selector as Selector | undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const hasSelector: string[] = [];
|
||||||
|
fields.forEach((field) => {
|
||||||
|
if ((field as any).fields) {
|
||||||
|
Object.entries((field as any).fields).forEach(([key, subField]) => {
|
||||||
|
if ((subField as any).selector) {
|
||||||
|
hasSelector.push(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (field.selector) {
|
||||||
|
hasSelector.push(field.key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...serviceDomains[domain][serviceName],
|
...serviceDomains[domain][serviceName],
|
||||||
fields,
|
fields,
|
||||||
hasSelector: fields.length
|
hasSelector,
|
||||||
? fields.filter((field) => field.selector).map((field) => field.key)
|
|
||||||
: [],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -35,10 +35,6 @@ import {
|
|||||||
computeDeviceName,
|
computeDeviceName,
|
||||||
} from "../data/device_registry";
|
} from "../data/device_registry";
|
||||||
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
|
import { EntityRegistryDisplayEntry } from "../data/entity_registry";
|
||||||
import {
|
|
||||||
FloorRegistryEntry,
|
|
||||||
subscribeFloorRegistry,
|
|
||||||
} from "../data/floor_registry";
|
|
||||||
import {
|
import {
|
||||||
LabelRegistryEntry,
|
LabelRegistryEntry,
|
||||||
subscribeLabelRegistry,
|
subscribeLabelRegistry,
|
||||||
@ -103,17 +99,12 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@query(".add-container", true) private _addContainer?: HTMLDivElement;
|
@query(".add-container", true) private _addContainer?: HTMLDivElement;
|
||||||
|
|
||||||
@state() private _floors?: FloorRegistryEntry[];
|
|
||||||
|
|
||||||
@state() private _labels?: LabelRegistryEntry[];
|
@state() private _labels?: LabelRegistryEntry[];
|
||||||
|
|
||||||
private _opened = false;
|
private _opened = false;
|
||||||
|
|
||||||
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
||||||
return [
|
return [
|
||||||
subscribeFloorRegistry(this.hass.connection, (floors) => {
|
|
||||||
this._floors = floors;
|
|
||||||
}),
|
|
||||||
subscribeLabelRegistry(this.hass.connection, (labels) => {
|
subscribeLabelRegistry(this.hass.connection, (labels) => {
|
||||||
this._labels = labels;
|
this._labels = labels;
|
||||||
}),
|
}),
|
||||||
@ -132,9 +123,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
<div class="mdc-chip-set items">
|
<div class="mdc-chip-set items">
|
||||||
${this.value?.floor_id
|
${this.value?.floor_id
|
||||||
? ensureArray(this.value.floor_id).map((floor_id) => {
|
? ensureArray(this.value.floor_id).map((floor_id) => {
|
||||||
const floor = this._floors?.find(
|
const floor = this.hass.floors[floor_id];
|
||||||
(flr) => flr.floor_id === floor_id
|
|
||||||
);
|
|
||||||
return this._renderChip(
|
return this._renderChip(
|
||||||
"floor_id",
|
"floor_id",
|
||||||
floor_id,
|
floor_id,
|
||||||
|
@ -109,7 +109,7 @@ export class HaTextField extends TextFieldBase {
|
|||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-text-field__icon {
|
.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ class HaWebRtcPlayer extends LitElement {
|
|||||||
let candidates = ""; // Build an Offer SDP string with ice candidates
|
let candidates = ""; // Build an Offer SDP string with ice candidates
|
||||||
const iceResolver = new Promise<void>((resolve) => {
|
const iceResolver = new Promise<void>((resolve) => {
|
||||||
peerConnection.addEventListener("icecandidate", async (event) => {
|
peerConnection.addEventListener("icecandidate", async (event) => {
|
||||||
if (!event.candidate) {
|
if (!event.candidate?.candidate) {
|
||||||
resolve(); // Gathering complete
|
resolve(); // Gathering complete
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { ensureArray } from "../../common/array/ensure-array";
|
import { ensureArray } from "../../common/array/ensure-array";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { Condition, Trigger } from "../../data/automation";
|
import { Condition, Trigger, flattenTriggers } from "../../data/automation";
|
||||||
import {
|
import {
|
||||||
Action,
|
Action,
|
||||||
ChooseAction,
|
ChooseAction,
|
||||||
@ -572,8 +572,8 @@ export class HatScriptGraph extends LitElement {
|
|||||||
const paths = Object.keys(this.trackedNodes);
|
const paths = Object.keys(this.trackedNodes);
|
||||||
const trigger_nodes =
|
const trigger_nodes =
|
||||||
"trigger" in this.trace.config
|
"trigger" in this.trace.config
|
||||||
? ensureArray(this.trace.config.trigger).map((trigger, i) =>
|
? flattenTriggers(ensureArray(this.trace.config.trigger)).map(
|
||||||
this.render_trigger(trigger, i)
|
(trigger, i) => this.render_trigger(trigger, i)
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
try {
|
try {
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
HassEntityBase,
|
HassEntityBase,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import { navigate } from "../common/navigate";
|
import { navigate } from "../common/navigate";
|
||||||
|
import { ensureArray } from "../common/array/ensure-array";
|
||||||
import { Context, HomeAssistant } from "../types";
|
import { Context, HomeAssistant } from "../types";
|
||||||
import { BlueprintInput } from "./blueprint";
|
import { BlueprintInput } from "./blueprint";
|
||||||
import { DeviceCondition, DeviceTrigger } from "./device_automation";
|
import { DeviceCondition, DeviceTrigger } from "./device_automation";
|
||||||
@ -62,6 +63,10 @@ export interface ContextConstraint {
|
|||||||
user_id?: string | string[];
|
user_id?: string | string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TriggerList {
|
||||||
|
triggers: Trigger | Trigger[] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export interface BaseTrigger {
|
export interface BaseTrigger {
|
||||||
alias?: string;
|
alias?: string;
|
||||||
platform: string;
|
platform: string;
|
||||||
@ -373,6 +378,27 @@ export const normalizeAutomationConfig = <
|
|||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const flattenTriggers = (
|
||||||
|
triggers: undefined | (Trigger | TriggerList)[]
|
||||||
|
): Trigger[] => {
|
||||||
|
if (!triggers) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const flatTriggers: Trigger[] = [];
|
||||||
|
|
||||||
|
triggers.forEach((t) => {
|
||||||
|
if ("triggers" in t) {
|
||||||
|
if (t.triggers) {
|
||||||
|
flatTriggers.push(...ensureArray(t.triggers));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
flatTriggers.push(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return flatTriggers;
|
||||||
|
};
|
||||||
|
|
||||||
export const showAutomationEditor = (data?: Partial<AutomationConfig>) => {
|
export const showAutomationEditor = (data?: Partial<AutomationConfig>) => {
|
||||||
initialAutomationEditorData = data;
|
initialAutomationEditorData = data;
|
||||||
navigate("/config/automation/edit/new");
|
navigate("/config/automation/edit/new");
|
||||||
|
@ -68,9 +68,18 @@ export const describeTrigger = (
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityRegistry: EntityRegistryEntry[],
|
entityRegistry: EntityRegistryEntry[],
|
||||||
ignoreAlias = false
|
ignoreAlias = false
|
||||||
) => {
|
): string => {
|
||||||
try {
|
try {
|
||||||
return tryDescribeTrigger(trigger, hass, entityRegistry, ignoreAlias);
|
const description = tryDescribeTrigger(
|
||||||
|
trigger,
|
||||||
|
hass,
|
||||||
|
entityRegistry,
|
||||||
|
ignoreAlias
|
||||||
|
);
|
||||||
|
if (typeof description !== "string") {
|
||||||
|
throw new Error(String(description));
|
||||||
|
}
|
||||||
|
return description;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -700,9 +709,18 @@ export const describeCondition = (
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityRegistry: EntityRegistryEntry[],
|
entityRegistry: EntityRegistryEntry[],
|
||||||
ignoreAlias = false
|
ignoreAlias = false
|
||||||
) => {
|
): string => {
|
||||||
try {
|
try {
|
||||||
return tryDescribeCondition(condition, hass, entityRegistry, ignoreAlias);
|
const description = tryDescribeCondition(
|
||||||
|
condition,
|
||||||
|
hass,
|
||||||
|
entityRegistry,
|
||||||
|
ignoreAlias
|
||||||
|
);
|
||||||
|
if (typeof description !== "string") {
|
||||||
|
throw new Error(String(description));
|
||||||
|
}
|
||||||
|
return description;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import type { IntegrationManifest, IntegrationType } from "./integration";
|
import type { IntegrationType } from "./integration";
|
||||||
|
|
||||||
export interface ConfigEntry {
|
export interface ConfigEntry {
|
||||||
entry_id: string;
|
entry_id: string;
|
||||||
@ -149,20 +149,19 @@ export const enableConfigEntry = (hass: HomeAssistant, configEntryId: string) =>
|
|||||||
|
|
||||||
export const sortConfigEntries = (
|
export const sortConfigEntries = (
|
||||||
configEntries: ConfigEntry[],
|
configEntries: ConfigEntry[],
|
||||||
manifestLookup: { [domain: string]: IntegrationManifest }
|
primaryConfigEntry: string | null
|
||||||
): ConfigEntry[] => {
|
): ConfigEntry[] => {
|
||||||
const sortedConfigEntries = [...configEntries];
|
if (!primaryConfigEntry) {
|
||||||
|
return configEntries;
|
||||||
const getScore = (entry: ConfigEntry) => {
|
}
|
||||||
const manifest = manifestLookup[entry.domain] as
|
const primaryEntry = configEntries.find(
|
||||||
| IntegrationManifest
|
(e) => e.entry_id === primaryConfigEntry
|
||||||
| undefined;
|
);
|
||||||
const isHelper = manifest?.integration_type === "helper";
|
if (!primaryEntry) {
|
||||||
return isHelper ? -1 : 1;
|
return configEntries;
|
||||||
};
|
}
|
||||||
|
const otherEntries = configEntries.filter(
|
||||||
const configEntriesCompare = (a: ConfigEntry, b: ConfigEntry) =>
|
(e) => e.entry_id !== primaryConfigEntry
|
||||||
getScore(b) - getScore(a);
|
);
|
||||||
|
return [primaryEntry, ...otherEntries];
|
||||||
return sortedConfigEntries.sort(configEntriesCompare);
|
|
||||||
};
|
};
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
export interface DataTableFilters {
|
export interface DataTableFilters {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
value: string[] | { key: string[] } | undefined;
|
value: DataTableFiltersValue;
|
||||||
items: Set<string> | undefined;
|
items: Set<string> | undefined;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DataTableFiltersValue = string[] | { key: string[] } | undefined;
|
||||||
|
|
||||||
|
export interface DataTableFiltersValues {
|
||||||
|
[key: string]: DataTableFiltersValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DataTableFiltersItems {
|
||||||
|
[key: string]: Set<string> | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
export const serializeFilters = (value: DataTableFilters) => {
|
export const serializeFilters = (value: DataTableFilters) => {
|
||||||
const serializedValue = {};
|
const serializedValue = {};
|
||||||
Object.entries(value).forEach(([key, val]) => {
|
Object.entries(value).forEach(([key, val]) => {
|
||||||
|
@ -33,6 +33,7 @@ export interface DeviceRegistryEntry extends RegistryEntry {
|
|||||||
entry_type: "service" | null;
|
entry_type: "service" | null;
|
||||||
disabled_by: "user" | "integration" | "config_entry" | null;
|
disabled_by: "user" | "integration" | "config_entry" | null;
|
||||||
configuration_url: string | null;
|
configuration_url: string | null;
|
||||||
|
primary_config_entry: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceEntityDisplayLookup {
|
export interface DeviceEntityDisplayLookup {
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import { Connection, createCollection } from "home-assistant-js-websocket";
|
|
||||||
import { Store } from "home-assistant-js-websocket/dist/store";
|
|
||||||
import { stringCompare } from "../common/string/compare";
|
import { stringCompare } from "../common/string/compare";
|
||||||
import { debounce } from "../common/util/debounce";
|
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { AreaRegistryEntry } from "./area_registry";
|
import { AreaRegistryEntry } from "./area_registry";
|
||||||
import { RegistryEntry } from "./registry";
|
import { RegistryEntry } from "./registry";
|
||||||
@ -27,48 +24,6 @@ export interface FloorRegistryEntryMutableParams {
|
|||||||
aliases?: string[];
|
aliases?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchFloorRegistry = (conn: Connection) =>
|
|
||||||
conn
|
|
||||||
.sendMessagePromise({
|
|
||||||
type: "config/floor_registry/list",
|
|
||||||
})
|
|
||||||
.then((floors) =>
|
|
||||||
(floors as FloorRegistryEntry[]).sort((ent1, ent2) => {
|
|
||||||
if (ent1.level !== ent2.level) {
|
|
||||||
return (ent1.level ?? 9999) - (ent2.level ?? 9999);
|
|
||||||
}
|
|
||||||
return stringCompare(ent1.name, ent2.name);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const subscribeFloorRegistryUpdates = (
|
|
||||||
conn: Connection,
|
|
||||||
store: Store<FloorRegistryEntry[]>
|
|
||||||
) =>
|
|
||||||
conn.subscribeEvents(
|
|
||||||
debounce(
|
|
||||||
() =>
|
|
||||||
fetchFloorRegistry(conn).then((areas: FloorRegistryEntry[]) =>
|
|
||||||
store.setState(areas, true)
|
|
||||||
),
|
|
||||||
500,
|
|
||||||
true
|
|
||||||
),
|
|
||||||
"floor_registry_updated"
|
|
||||||
);
|
|
||||||
|
|
||||||
export const subscribeFloorRegistry = (
|
|
||||||
conn: Connection,
|
|
||||||
onChange: (floors: FloorRegistryEntry[]) => void
|
|
||||||
) =>
|
|
||||||
createCollection<FloorRegistryEntry[]>(
|
|
||||||
"_floorRegistry",
|
|
||||||
fetchFloorRegistry,
|
|
||||||
subscribeFloorRegistryUpdates,
|
|
||||||
conn,
|
|
||||||
onChange
|
|
||||||
);
|
|
||||||
|
|
||||||
export const createFloorRegistryEntry = (
|
export const createFloorRegistryEntry = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
values: FloorRegistryEntryMutableParams
|
values: FloorRegistryEntryMutableParams
|
||||||
|
@ -2,6 +2,8 @@ import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
|||||||
import { navigate } from "../common/navigate";
|
import { navigate } from "../common/navigate";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import { subscribeDeviceRegistry } from "./device_registry";
|
import { subscribeDeviceRegistry } from "./device_registry";
|
||||||
|
import { isComponentLoaded } from "../common/config/is_component_loaded";
|
||||||
|
import { getThreadDataSetTLV, listThreadDataSets } from "./thread";
|
||||||
|
|
||||||
export enum NetworkType {
|
export enum NetworkType {
|
||||||
THREAD = "thread",
|
THREAD = "thread",
|
||||||
@ -51,10 +53,30 @@ export interface MatterCommissioningParameters {
|
|||||||
export const canCommissionMatterExternal = (hass: HomeAssistant) =>
|
export const canCommissionMatterExternal = (hass: HomeAssistant) =>
|
||||||
hass.auth.external?.config.canCommissionMatter;
|
hass.auth.external?.config.canCommissionMatter;
|
||||||
|
|
||||||
export const startExternalCommissioning = (hass: HomeAssistant) =>
|
export const startExternalCommissioning = async (hass: HomeAssistant) => {
|
||||||
hass.auth.external!.fireMessage({
|
if (isComponentLoaded(hass, "thread")) {
|
||||||
|
const datasets = await listThreadDataSets(hass);
|
||||||
|
const preferredDataset = datasets.datasets.find(
|
||||||
|
(dataset) => dataset.preferred
|
||||||
|
);
|
||||||
|
if (preferredDataset) {
|
||||||
|
return hass.auth.external!.fireMessage({
|
||||||
|
type: "matter/commission",
|
||||||
|
payload: {
|
||||||
|
active_operational_dataset: (
|
||||||
|
await getThreadDataSetTLV(hass, preferredDataset.dataset_id)
|
||||||
|
).tlv,
|
||||||
|
border_agent_id: preferredDataset.preferred_border_agent_id,
|
||||||
|
mac_extended_address: preferredDataset.preferred_extended_address,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hass.auth.external!.fireMessage({
|
||||||
type: "matter/commission",
|
type: "matter/commission",
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const redirectOnNewMatterDevice = (
|
export const redirectOnNewMatterDevice = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -50,7 +50,7 @@ export const describeAction = <T extends ActionType>(
|
|||||||
ignoreAlias = false
|
ignoreAlias = false
|
||||||
): string => {
|
): string => {
|
||||||
try {
|
try {
|
||||||
return tryDescribeAction(
|
const description = tryDescribeAction(
|
||||||
hass,
|
hass,
|
||||||
entityRegistry,
|
entityRegistry,
|
||||||
labelRegistry,
|
labelRegistry,
|
||||||
@ -59,6 +59,10 @@ export const describeAction = <T extends ActionType>(
|
|||||||
actionType,
|
actionType,
|
||||||
ignoreAlias
|
ignoreAlias
|
||||||
);
|
);
|
||||||
|
if (typeof description !== "string") {
|
||||||
|
throw new Error(String(description));
|
||||||
|
}
|
||||||
|
return description;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -3,6 +3,7 @@ import { Context, HomeAssistant } from "../types";
|
|||||||
import {
|
import {
|
||||||
BlueprintAutomationConfig,
|
BlueprintAutomationConfig,
|
||||||
ManualAutomationConfig,
|
ManualAutomationConfig,
|
||||||
|
flattenTriggers,
|
||||||
} from "./automation";
|
} from "./automation";
|
||||||
import { BlueprintScriptConfig, ScriptConfig } from "./script";
|
import { BlueprintScriptConfig, ScriptConfig } from "./script";
|
||||||
|
|
||||||
@ -190,7 +191,11 @@ export const getDataFromPath = (
|
|||||||
if (!tempResult && raw === "sequence") {
|
if (!tempResult && raw === "sequence") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (raw === "trigger") {
|
||||||
|
result = flattenTriggers(tempResult);
|
||||||
|
} else {
|
||||||
result = tempResult;
|
result = tempResult;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
src/data/ws-floor_registry.ts
Normal file
47
src/data/ws-floor_registry.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { Connection, createCollection } from "home-assistant-js-websocket";
|
||||||
|
import { Store } from "home-assistant-js-websocket/dist/store";
|
||||||
|
import { stringCompare } from "../common/string/compare";
|
||||||
|
import { debounce } from "../common/util/debounce";
|
||||||
|
import { FloorRegistryEntry } from "./floor_registry";
|
||||||
|
|
||||||
|
const fetchFloorRegistry = (conn: Connection) =>
|
||||||
|
conn
|
||||||
|
.sendMessagePromise({
|
||||||
|
type: "config/floor_registry/list",
|
||||||
|
})
|
||||||
|
.then((floors) =>
|
||||||
|
(floors as FloorRegistryEntry[]).sort((ent1, ent2) => {
|
||||||
|
if (ent1.level !== ent2.level) {
|
||||||
|
return (ent1.level ?? 9999) - (ent2.level ?? 9999);
|
||||||
|
}
|
||||||
|
return stringCompare(ent1.name, ent2.name);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const subscribeFloorRegistryUpdates = (
|
||||||
|
conn: Connection,
|
||||||
|
store: Store<FloorRegistryEntry[]>
|
||||||
|
) =>
|
||||||
|
conn.subscribeEvents(
|
||||||
|
debounce(
|
||||||
|
() =>
|
||||||
|
fetchFloorRegistry(conn).then((areas: FloorRegistryEntry[]) =>
|
||||||
|
store.setState(areas, true)
|
||||||
|
),
|
||||||
|
500,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
"floor_registry_updated"
|
||||||
|
);
|
||||||
|
|
||||||
|
export const subscribeFloorRegistry = (
|
||||||
|
conn: Connection,
|
||||||
|
onChange: (floors: FloorRegistryEntry[]) => void
|
||||||
|
) =>
|
||||||
|
createCollection<FloorRegistryEntry[]>(
|
||||||
|
"_floorRegistry",
|
||||||
|
fetchFloorRegistry,
|
||||||
|
subscribeFloorRegistryUpdates,
|
||||||
|
conn,
|
||||||
|
onChange
|
||||||
|
);
|
@ -252,6 +252,7 @@ export interface ZWaveJSNodeConfigParamMetadata {
|
|||||||
type: string;
|
type: string;
|
||||||
unit: string;
|
unit: string;
|
||||||
states: { [key: number]: string };
|
states: { [key: number]: string };
|
||||||
|
default: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ZWaveJSSetConfigParamData {
|
export interface ZWaveJSSetConfigParamData {
|
||||||
|
@ -83,7 +83,7 @@ export const showConfigFlowDialog = (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const prefix = options?.path?.[0] ? `sections.${options.path[0]}` : "";
|
const prefix = options?.path?.[0] ? `sections.${options.path[0]}.` : "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
hass.localize(
|
hass.localize(
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
|
||||||
import { mdiAlertOutline } from "@mdi/js";
|
import { mdiAlertOutline } from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../../components/ha-dialog";
|
import "../../components/ha-md-dialog";
|
||||||
|
import type { HaMdDialog } from "../../components/ha-md-dialog";
|
||||||
|
import "../../components/ha-dialog-header";
|
||||||
import "../../components/ha-svg-icon";
|
import "../../components/ha-svg-icon";
|
||||||
import "../../components/ha-switch";
|
import "../../components/ha-button";
|
||||||
import { HaTextField } from "../../components/ha-textfield";
|
import { HaTextField } from "../../components/ha-textfield";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { DialogBoxParams } from "./show-dialog-box";
|
import { DialogBoxParams } from "./show-dialog-box";
|
||||||
@ -18,8 +19,12 @@ class DialogBox extends LitElement {
|
|||||||
|
|
||||||
@state() private _params?: DialogBoxParams;
|
@state() private _params?: DialogBoxParams;
|
||||||
|
|
||||||
|
@state() private _closeState?: "canceled" | "confirmed";
|
||||||
|
|
||||||
@query("ha-textfield") private _textField?: HaTextField;
|
@query("ha-textfield") private _textField?: HaTextField;
|
||||||
|
|
||||||
|
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||||
|
|
||||||
public async showDialog(params: DialogBoxParams): Promise<void> {
|
public async showDialog(params: DialogBoxParams): Promise<void> {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
}
|
}
|
||||||
@ -42,33 +47,33 @@ class DialogBox extends LitElement {
|
|||||||
|
|
||||||
const confirmPrompt = this._params.confirmation || this._params.prompt;
|
const confirmPrompt = this._params.confirmation || this._params.prompt;
|
||||||
|
|
||||||
|
const dialogTitle =
|
||||||
|
this._params.title ||
|
||||||
|
(this._params.confirmation &&
|
||||||
|
this.hass.localize("ui.dialogs.generic.default_confirmation_title"));
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-md-dialog
|
||||||
open
|
open
|
||||||
?scrimClickAction=${confirmPrompt}
|
.disableCancelAction=${confirmPrompt || false}
|
||||||
?escapeKeyAction=${confirmPrompt}
|
|
||||||
@closed=${this._dialogClosed}
|
@closed=${this._dialogClosed}
|
||||||
defaultAction="ignore"
|
type="alert"
|
||||||
.heading=${html`${this._params.warning
|
aria-labelledby="dialog-box-title"
|
||||||
|
aria-describedby="dialog-box-description"
|
||||||
|
>
|
||||||
|
<div slot="headline">
|
||||||
|
<span .title=${dialogTitle} id="dialog-box-title">
|
||||||
|
${this._params.warning
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
.path=${mdiAlertOutline}
|
.path=${mdiAlertOutline}
|
||||||
style="color: var(--warning-color)"
|
style="color: var(--warning-color)"
|
||||||
></ha-svg-icon> `
|
></ha-svg-icon> `
|
||||||
: ""}${this._params.title
|
: nothing}
|
||||||
? this._params.title
|
${dialogTitle}
|
||||||
: this._params.confirmation &&
|
</span>
|
||||||
this.hass.localize(
|
</div>
|
||||||
"ui.dialogs.generic.default_confirmation_title"
|
<div slot="content" id="dialog-box-description">
|
||||||
)}`}
|
${this._params.text ? html` <p>${this._params.text}</p> ` : ""}
|
||||||
>
|
|
||||||
<div>
|
|
||||||
${this._params.text
|
|
||||||
? html`
|
|
||||||
<p class=${this._params.prompt ? "no-bottom-padding" : ""}>
|
|
||||||
${this._params.text}
|
|
||||||
</p>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this._params.prompt
|
${this._params.prompt
|
||||||
? html`
|
? html`
|
||||||
<ha-textfield
|
<ha-textfield
|
||||||
@ -87,24 +92,23 @@ class DialogBox extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
|
<div slot="actions">
|
||||||
${confirmPrompt &&
|
${confirmPrompt &&
|
||||||
html`
|
html`
|
||||||
<mwc-button
|
<ha-button
|
||||||
@click=${this._dismiss}
|
@click=${this._dismiss}
|
||||||
slot="secondaryAction"
|
|
||||||
?dialogInitialFocus=${!this._params.prompt &&
|
?dialogInitialFocus=${!this._params.prompt &&
|
||||||
this._params.destructive}
|
this._params.destructive}
|
||||||
>
|
>
|
||||||
${this._params.dismissText
|
${this._params.dismissText
|
||||||
? this._params.dismissText
|
? this._params.dismissText
|
||||||
: this.hass.localize("ui.dialogs.generic.cancel")}
|
: this.hass.localize("ui.dialogs.generic.cancel")}
|
||||||
</mwc-button>
|
</ha-button>
|
||||||
`}
|
`}
|
||||||
<mwc-button
|
<ha-button
|
||||||
@click=${this._confirm}
|
@click=${this._confirm}
|
||||||
?dialogInitialFocus=${!this._params.prompt &&
|
?dialogInitialFocus=${!this._params.prompt &&
|
||||||
!this._params.destructive}
|
!this._params.destructive}
|
||||||
slot="primaryAction"
|
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
destructive: this._params.destructive || false,
|
destructive: this._params.destructive || false,
|
||||||
})}
|
})}
|
||||||
@ -112,33 +116,40 @@ class DialogBox extends LitElement {
|
|||||||
${this._params.confirmText
|
${this._params.confirmText
|
||||||
? this._params.confirmText
|
? this._params.confirmText
|
||||||
: this.hass.localize("ui.dialogs.generic.ok")}
|
: this.hass.localize("ui.dialogs.generic.ok")}
|
||||||
</mwc-button>
|
</ha-button>
|
||||||
</ha-dialog>
|
</div>
|
||||||
|
</ha-md-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dismiss(): void {
|
private _cancel(): void {
|
||||||
if (this._params?.cancel) {
|
if (this._params?.cancel) {
|
||||||
this._params.cancel();
|
this._params.cancel();
|
||||||
}
|
}
|
||||||
this._close();
|
}
|
||||||
|
|
||||||
|
private _dismiss(): void {
|
||||||
|
this._cancel();
|
||||||
|
this._closeState = "canceled";
|
||||||
|
this._closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _confirm(): void {
|
private _confirm(): void {
|
||||||
if (this._params!.confirm) {
|
if (this._params!.confirm) {
|
||||||
this._params!.confirm(this._textField?.value);
|
this._params!.confirm(this._textField?.value);
|
||||||
}
|
}
|
||||||
this._close();
|
this._closeState = "confirmed";
|
||||||
|
this._closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _dialogClosed(ev) {
|
private _closeDialog() {
|
||||||
if (ev.detail.action === "ignore") {
|
this._dialog?.close();
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._dismiss();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _close(): void {
|
private _dialogClosed() {
|
||||||
|
if (!this._closeState) {
|
||||||
|
this._cancel();
|
||||||
|
}
|
||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -168,10 +179,6 @@ class DialogBox extends LitElement {
|
|||||||
.destructive {
|
.destructive {
|
||||||
--mdc-theme-primary: var(--error-color);
|
--mdc-theme-primary: var(--error-color);
|
||||||
}
|
}
|
||||||
ha-dialog {
|
|
||||||
/* Place above other dialogs */
|
|
||||||
--dialog-z-index: 104;
|
|
||||||
}
|
|
||||||
@media all and (min-width: 600px) {
|
@media all and (min-width: 600px) {
|
||||||
ha-dialog {
|
ha-dialog {
|
||||||
--mdc-dialog-min-width: 400px;
|
--mdc-dialog-min-width: 400px;
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state, query } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-dialog";
|
import {
|
||||||
|
getMobileOpenFromBottomAnimation,
|
||||||
|
getMobileCloseToBottomAnimation,
|
||||||
|
} from "../../../../components/ha-md-dialog";
|
||||||
|
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
|
||||||
import "../../../../components/ha-dialog-header";
|
import "../../../../components/ha-dialog-header";
|
||||||
import "../../../../components/ha-icon-button-toggle";
|
import "../../../../components/ha-icon-button-toggle";
|
||||||
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
|
||||||
import {
|
import {
|
||||||
formatTempColor,
|
|
||||||
LightColor,
|
LightColor,
|
||||||
LightColorMode,
|
LightColorMode,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
@ -38,15 +41,7 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
|
|
||||||
@state() private _modes: LightPickerMode[] = [];
|
@state() private _modes: LightPickerMode[] = [];
|
||||||
|
|
||||||
@state() private _currentValue?: string;
|
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||||
|
|
||||||
private _colorHovered(ev: CustomEvent<HASSDomEvents["color-hovered"]>) {
|
|
||||||
if (ev.detail && "color_temp_kelvin" in ev.detail) {
|
|
||||||
this._currentValue = formatTempColor(ev.detail.color_temp_kelvin);
|
|
||||||
} else {
|
|
||||||
this._currentValue = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async showDialog(
|
public async showDialog(
|
||||||
dialogParams: LightColorFavoriteDialogParams
|
dialogParams: LightColorFavoriteDialogParams
|
||||||
@ -58,10 +53,7 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog(): void {
|
public closeDialog(): void {
|
||||||
this._dialogParams = undefined;
|
this._dialog?.close();
|
||||||
this._entry = undefined;
|
|
||||||
this._color = undefined;
|
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateModes() {
|
private _updateModes() {
|
||||||
@ -130,9 +122,20 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
|
|
||||||
private async _cancel() {
|
private async _cancel() {
|
||||||
this._dialogParams?.cancel?.();
|
this._dialogParams?.cancel?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cancelDialog() {
|
||||||
|
this._cancel();
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _dialogClosed(): void {
|
||||||
|
this._dialogParams = undefined;
|
||||||
|
this._entry = undefined;
|
||||||
|
this._color = undefined;
|
||||||
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
|
}
|
||||||
|
|
||||||
private async _save() {
|
private async _save() {
|
||||||
if (!this._color) {
|
if (!this._color) {
|
||||||
this._cancel();
|
this._cancel();
|
||||||
@ -156,23 +159,27 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-md-dialog
|
||||||
open
|
open
|
||||||
@closed=${this._cancel}
|
@cancel=${this._cancel}
|
||||||
.heading=${this._dialogParams?.title ?? ""}
|
@closed=${this._dialogClosed}
|
||||||
flexContent
|
aria-labelledby="dialog-light-color-favorite-title"
|
||||||
|
.getOpenAnimation=${getMobileOpenFromBottomAnimation}
|
||||||
|
.getCloseAnimation=${getMobileCloseToBottomAnimation}
|
||||||
>
|
>
|
||||||
<ha-dialog-header slot="heading">
|
<ha-dialog-header slot="headline">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="navigationIcon"
|
slot="navigationIcon"
|
||||||
dialogAction="cancel"
|
@click=${this.closeDialog}
|
||||||
.label=${this.hass.localize("ui.common.close")}
|
.label=${this.hass.localize("ui.common.close")}
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<span slot="title">${this._dialogParams?.title}</span>
|
<span slot="title" id="dialog-light-color-favorite-title"
|
||||||
|
>${this._dialogParams?.title}</span
|
||||||
|
>
|
||||||
</ha-dialog-header>
|
</ha-dialog-header>
|
||||||
|
<div slot="content">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<span class="value">${this._currentValue}</span>
|
|
||||||
${this._modes.length > 1
|
${this._modes.length > 1
|
||||||
? html`
|
? html`
|
||||||
<div class="modes">
|
<div class="modes">
|
||||||
@ -197,7 +204,6 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
${this._mode === "color_temp"
|
${this._mode === "color_temp"
|
||||||
? html`
|
? html`
|
||||||
@ -205,7 +211,6 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
@color-changed=${this._colorChanged}
|
@color-changed=${this._colorChanged}
|
||||||
@color-hovered=${this._colorHovered}
|
|
||||||
>
|
>
|
||||||
</light-color-temp-picker>
|
</light-color-temp-picker>
|
||||||
`
|
`
|
||||||
@ -216,22 +221,21 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
@color-changed=${this._colorChanged}
|
@color-changed=${this._colorChanged}
|
||||||
@color-hovered=${this._colorHovered}
|
|
||||||
>
|
>
|
||||||
</light-color-rgb-picker>
|
</light-color-rgb-picker>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
<ha-button slot="secondaryAction" dialogAction="cancel">
|
</div>
|
||||||
|
<div slot="actions">
|
||||||
|
<ha-button @click=${this._cancelDialog}>
|
||||||
${this.hass.localize("ui.common.cancel")}
|
${this.hass.localize("ui.common.cancel")}
|
||||||
</ha-button>
|
</ha-button>
|
||||||
<ha-button
|
<ha-button @click=${this._save} .disabled=${!this._color}
|
||||||
slot="primaryAction"
|
|
||||||
@click=${this._save}
|
|
||||||
.disabled=${!this._color}
|
|
||||||
>${this.hass.localize("ui.common.save")}</ha-button
|
>${this.hass.localize("ui.common.save")}</ha-button
|
||||||
>
|
>
|
||||||
</ha-dialog>
|
</div>
|
||||||
|
</ha-md-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,19 +243,23 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
css`
|
css`
|
||||||
ha-dialog {
|
ha-md-dialog {
|
||||||
--dialog-content-padding: 0;
|
min-width: 420px; /* prevent width jumps when switching modes */
|
||||||
|
max-height: min(
|
||||||
|
600px,
|
||||||
|
100% - 48px
|
||||||
|
); /* prevent scrolling on desktop */
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
ha-dialog {
|
ha-md-dialog {
|
||||||
--dialog-surface-margin-top: 100px;
|
min-width: 100%;
|
||||||
--mdc-dialog-min-height: auto;
|
min-height: auto;
|
||||||
--mdc-dialog-max-height: calc(100% - 100px);
|
max-height: calc(100% - 100px);
|
||||||
--ha-dialog-border-radius: var(
|
margin-bottom: 0;
|
||||||
--ha-dialog-bottom-sheet-border-radius,
|
|
||||||
28px 28px 0 0
|
--md-dialog-container-shape-start-start: 28px;
|
||||||
);
|
--md-dialog-container-shape-start-end: 28px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,21 +295,6 @@ class DialogLightColorFavorite extends LitElement {
|
|||||||
rgb(255, 160, 0) 100%
|
rgb(255, 160, 0) 100%
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
.value {
|
|
||||||
pointer-events: none;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: auto;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
height: 48px;
|
|
||||||
line-height: 48px;
|
|
||||||
letter-spacing: 0.1px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import { isUnavailableState } from "../../../data/entity";
|
|||||||
import { computeObjectId } from "../../../common/entity/compute_object_id";
|
import { computeObjectId } from "../../../common/entity/compute_object_id";
|
||||||
import { listenMediaQuery } from "../../../common/dom/media_query";
|
import { listenMediaQuery } from "../../../common/dom/media_query";
|
||||||
import "../components/ha-more-info-state-header";
|
import "../components/ha-more-info-state-header";
|
||||||
|
import { ExtEntityRegistryEntry } from "../../../data/entity_registry";
|
||||||
|
|
||||||
@customElement("more-info-script")
|
@customElement("more-info-script")
|
||||||
class MoreInfoScript extends LitElement {
|
class MoreInfoScript extends LitElement {
|
||||||
@ -28,6 +29,8 @@ class MoreInfoScript extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public stateObj?: ScriptEntity;
|
@property({ attribute: false }) public stateObj?: ScriptEntity;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public entry?: ExtEntityRegistryEntry;
|
||||||
|
|
||||||
@state() private _scriptData: Record<string, any> = {};
|
@state() private _scriptData: Record<string, any> = {};
|
||||||
|
|
||||||
@state() private narrow = false;
|
@state() private narrow = false;
|
||||||
@ -59,8 +62,9 @@ class MoreInfoScript extends LitElement {
|
|||||||
const stateObj = this.stateObj;
|
const stateObj = this.stateObj;
|
||||||
|
|
||||||
const fields =
|
const fields =
|
||||||
this.hass.services.script[computeObjectId(this.stateObj.entity_id)]
|
this.hass.services.script[
|
||||||
?.fields;
|
this.entry?.unique_id || computeObjectId(this.stateObj.entity_id)
|
||||||
|
]?.fields;
|
||||||
|
|
||||||
const hasFields = fields && Object.keys(fields).length > 0;
|
const hasFields = fields && Object.keys(fields).length > 0;
|
||||||
|
|
||||||
@ -138,17 +142,30 @@ class MoreInfoScript extends LitElement {
|
|||||||
protected override willUpdate(changedProperties: PropertyValues): void {
|
protected override willUpdate(changedProperties: PropertyValues): void {
|
||||||
super.willUpdate(changedProperties);
|
super.willUpdate(changedProperties);
|
||||||
|
|
||||||
if (!changedProperties.has("stateObj")) {
|
if (changedProperties.has("stateObj")) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldState = changedProperties.get("stateObj") as
|
const oldState = changedProperties.get("stateObj") as
|
||||||
| HassEntity
|
| HassEntity
|
||||||
| undefined;
|
| undefined;
|
||||||
const newState = this.stateObj;
|
const newState = this.stateObj;
|
||||||
|
|
||||||
if (newState && (!oldState || oldState.entity_id !== newState.entity_id)) {
|
if (
|
||||||
this._scriptData = { action: newState.entity_id, data: {} };
|
newState &&
|
||||||
|
(!oldState || oldState.entity_id !== newState.entity_id)
|
||||||
|
) {
|
||||||
|
this._scriptData = {
|
||||||
|
action:
|
||||||
|
this.entry?.entity_id === newState.entity_id
|
||||||
|
? `script.${this.entry.unique_id}`
|
||||||
|
: newState.entity_id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.entry?.unique_id && changedProperties.has("entry")) {
|
||||||
|
const action = `script.${this.entry?.unique_id}`;
|
||||||
|
if (this._scriptData?.action !== action) {
|
||||||
|
this._scriptData = { ...this._scriptData, action };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +178,7 @@ class MoreInfoScript extends LitElement {
|
|||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.hass.callService(
|
this.hass.callService(
|
||||||
"script",
|
"script",
|
||||||
computeObjectId(this.stateObj!.entity_id),
|
this.entry?.unique_id || computeObjectId(this.stateObj!.entity_id),
|
||||||
this._scriptData.data
|
this._scriptData.data
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import { subscribeRepairsIssueRegistry } from "../data/repairs";
|
|||||||
import { subscribeAreaRegistry } from "../data/ws-area_registry";
|
import { subscribeAreaRegistry } from "../data/ws-area_registry";
|
||||||
import { subscribeDeviceRegistry } from "../data/ws-device_registry";
|
import { subscribeDeviceRegistry } from "../data/ws-device_registry";
|
||||||
import { subscribeEntityRegistryDisplay } from "../data/ws-entity_registry_display";
|
import { subscribeEntityRegistryDisplay } from "../data/ws-entity_registry_display";
|
||||||
|
import { subscribeFloorRegistry } from "../data/ws-floor_registry";
|
||||||
import { subscribePanels } from "../data/ws-panels";
|
import { subscribePanels } from "../data/ws-panels";
|
||||||
import { subscribeThemes } from "../data/ws-themes";
|
import { subscribeThemes } from "../data/ws-themes";
|
||||||
import { subscribeUser } from "../data/ws-user";
|
import { subscribeUser } from "../data/ws-user";
|
||||||
@ -117,6 +118,7 @@ window.hassConnection.then(({ conn }) => {
|
|||||||
subscribeEntityRegistryDisplay(conn, noop);
|
subscribeEntityRegistryDisplay(conn, noop);
|
||||||
subscribeDeviceRegistry(conn, noop);
|
subscribeDeviceRegistry(conn, noop);
|
||||||
subscribeAreaRegistry(conn, noop);
|
subscribeAreaRegistry(conn, noop);
|
||||||
|
subscribeFloorRegistry(conn, noop);
|
||||||
subscribeConfig(conn, noop);
|
subscribeConfig(conn, noop);
|
||||||
subscribeServices(conn, noop);
|
subscribeServices(conn, noop);
|
||||||
subscribePanels(conn, noop);
|
subscribePanels(conn, noop);
|
||||||
|
@ -57,6 +57,11 @@ interface EMOutgoingMessageBarCodeNotify extends EMMessage {
|
|||||||
|
|
||||||
interface EMOutgoingMessageMatterCommission extends EMMessage {
|
interface EMOutgoingMessageMatterCommission extends EMMessage {
|
||||||
type: "matter/commission";
|
type: "matter/commission";
|
||||||
|
payload?: {
|
||||||
|
mac_extended_address: string | null;
|
||||||
|
border_agent_id: string | null;
|
||||||
|
active_operational_dataset: string | null;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EMOutgoingMessageImportThreadCredentials extends EMMessage {
|
interface EMOutgoingMessageImportThreadCredentials extends EMMessage {
|
||||||
@ -136,7 +141,7 @@ interface EMOutgoingMessageThreadStoreInPlatformKeychain extends EMMessage {
|
|||||||
type: "thread/store_in_platform_keychain";
|
type: "thread/store_in_platform_keychain";
|
||||||
payload: {
|
payload: {
|
||||||
mac_extended_address: string;
|
mac_extended_address: string;
|
||||||
border_agent_id: string | null;
|
border_agent_id: string;
|
||||||
active_operational_dataset: string;
|
active_operational_dataset: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,10 @@ import type {
|
|||||||
HaDataTable,
|
HaDataTable,
|
||||||
SortingDirection,
|
SortingDirection,
|
||||||
} from "../components/data-table/ha-data-table";
|
} from "../components/data-table/ha-data-table";
|
||||||
import "../components/ha-button-menu-new";
|
import "../components/ha-md-button-menu";
|
||||||
import "../components/ha-dialog";
|
import "../components/ha-dialog";
|
||||||
import { HaMenu } from "../components/ha-menu";
|
import { HaMenu } from "../components/ha-menu";
|
||||||
import "../components/ha-menu-item";
|
import "../components/ha-md-menu-item";
|
||||||
import "../components/search-input-outlined";
|
import "../components/search-input-outlined";
|
||||||
import type { HomeAssistant, Route } from "../types";
|
import type { HomeAssistant, Route } from "../types";
|
||||||
import "./hass-tabs-subpage";
|
import "./hass-tabs-subpage";
|
||||||
@ -330,7 +330,7 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
"ui.components.subpage-data-table.exit_selection_mode"
|
"ui.components.subpage-data-table.exit_selection_mode"
|
||||||
)}
|
)}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-button-menu-new positioning="absolute">
|
<ha-md-button-menu positioning="absolute">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
.label=${localize(
|
.label=${localize(
|
||||||
"ui.components.subpage-data-table.select"
|
"ui.components.subpage-data-table.select"
|
||||||
@ -346,20 +346,26 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
.path=${mdiMenuDown}
|
.path=${mdiMenuDown}
|
||||||
></ha-svg-icon
|
></ha-svg-icon
|
||||||
></ha-assist-chip>
|
></ha-assist-chip>
|
||||||
<ha-menu-item .value=${undefined} @click=${this._selectAll}>
|
<ha-md-menu-item
|
||||||
|
.value=${undefined}
|
||||||
|
@click=${this._selectAll}
|
||||||
|
>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${localize("ui.components.subpage-data-table.select_all")}
|
${localize("ui.components.subpage-data-table.select_all")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item .value=${undefined} @click=${this._selectNone}>
|
<ha-md-menu-item
|
||||||
|
.value=${undefined}
|
||||||
|
@click=${this._selectNone}
|
||||||
|
>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${localize(
|
${localize(
|
||||||
"ui.components.subpage-data-table.select_none"
|
"ui.components.subpage-data-table.select_none"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
.value=${undefined}
|
.value=${undefined}
|
||||||
@click=${this._disableSelectMode}
|
@click=${this._disableSelectMode}
|
||||||
>
|
>
|
||||||
@ -368,8 +374,8 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
"ui.components.subpage-data-table.close_select_mode"
|
"ui.components.subpage-data-table.close_select_mode"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
<p>
|
<p>
|
||||||
${localize("ui.components.subpage-data-table.selected", {
|
${localize("ui.components.subpage-data-table.selected", {
|
||||||
selected: this.selected || "0",
|
selected: this.selected || "0",
|
||||||
@ -476,27 +482,27 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
${Object.entries(this.columns).map(([id, column]) =>
|
${Object.entries(this.columns).map(([id, column]) =>
|
||||||
column.groupable
|
column.groupable
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
.value=${id}
|
.value=${id}
|
||||||
@click=${this._handleGroupBy}
|
@click=${this._handleGroupBy}
|
||||||
.selected=${id === this._groupColumn}
|
.selected=${id === this._groupColumn}
|
||||||
class=${classMap({ selected: id === this._groupColumn })}
|
class=${classMap({ selected: id === this._groupColumn })}
|
||||||
>
|
>
|
||||||
${column.title || column.label}
|
${column.title || column.label}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing
|
: nothing
|
||||||
)}
|
)}
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
.value=${undefined}
|
.value=${undefined}
|
||||||
@click=${this._handleGroupBy}
|
@click=${this._handleGroupBy}
|
||||||
.selected=${this._groupColumn === undefined}
|
.selected=${this._groupColumn === undefined}
|
||||||
class=${classMap({ selected: this._groupColumn === undefined })}
|
class=${classMap({ selected: this._groupColumn === undefined })}
|
||||||
>
|
>
|
||||||
${localize("ui.components.subpage-data-table.dont_group_by")}
|
${localize("ui.components.subpage-data-table.dont_group_by")}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
@click=${this._collapseAllGroups}
|
@click=${this._collapseAllGroups}
|
||||||
.disabled=${this._groupColumn === undefined}
|
.disabled=${this._groupColumn === undefined}
|
||||||
>
|
>
|
||||||
@ -505,8 +511,8 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
.path=${mdiUnfoldLessHorizontal}
|
.path=${mdiUnfoldLessHorizontal}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${localize("ui.components.subpage-data-table.collapse_all_groups")}
|
${localize("ui.components.subpage-data-table.collapse_all_groups")}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
@click=${this._expandAllGroups}
|
@click=${this._expandAllGroups}
|
||||||
.disabled=${this._groupColumn === undefined}
|
.disabled=${this._groupColumn === undefined}
|
||||||
>
|
>
|
||||||
@ -515,13 +521,13 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
.path=${mdiUnfoldMoreHorizontal}
|
.path=${mdiUnfoldMoreHorizontal}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${localize("ui.components.subpage-data-table.expand_all_groups")}
|
${localize("ui.components.subpage-data-table.expand_all_groups")}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
</ha-menu>
|
</ha-menu>
|
||||||
<ha-menu anchor="sort-by-anchor" id="sort-by-menu" positioning="fixed">
|
<ha-menu anchor="sort-by-anchor" id="sort-by-menu" positioning="fixed">
|
||||||
${Object.entries(this.columns).map(([id, column]) =>
|
${Object.entries(this.columns).map(([id, column]) =>
|
||||||
column.sortable
|
column.sortable
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
.value=${id}
|
.value=${id}
|
||||||
@click=${this._handleSortBy}
|
@click=${this._handleSortBy}
|
||||||
keep-open
|
keep-open
|
||||||
@ -539,7 +545,7 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
${column.title || column.label}
|
${column.title || column.label}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing
|
: nothing
|
||||||
)}
|
)}
|
||||||
@ -893,7 +899,7 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
|
|
||||||
#sort-by-anchor,
|
#sort-by-anchor,
|
||||||
#group-by-anchor,
|
#group-by-anchor,
|
||||||
ha-button-menu-new ha-assist-chip {
|
ha-md-button-menu ha-assist-chip {
|
||||||
--md-assist-chip-trailing-space: 8px;
|
--md-assist-chip-trailing-space: 8px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@ -13,6 +12,7 @@ import { isComponentLoaded } from "../common/config/is_component_loaded";
|
|||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { stringCompare } from "../common/string/compare";
|
import { stringCompare } from "../common/string/compare";
|
||||||
import { LocalizeFunc } from "../common/translations/localize";
|
import { LocalizeFunc } from "../common/translations/localize";
|
||||||
|
import "../components/ha-button";
|
||||||
import { ConfigEntry, subscribeConfigEntries } from "../data/config_entries";
|
import { ConfigEntry, subscribeConfigEntries } from "../data/config_entries";
|
||||||
import { subscribeConfigFlowInProgress } from "../data/config_flow";
|
import { subscribeConfigFlowInProgress } from "../data/config_flow";
|
||||||
import { domainToName } from "../data/integration";
|
import { domainToName } from "../data/integration";
|
||||||
@ -117,6 +117,30 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
const foundIntegrations = domains.length;
|
const foundIntegrations = domains.length;
|
||||||
|
|
||||||
|
// there is a possibility that the user has no integrations
|
||||||
|
if (foundIntegrations === 0) {
|
||||||
|
return html`
|
||||||
|
<div class="all-set-icon">🎉</div>
|
||||||
|
<h1>
|
||||||
|
${this.onboardingLocalize(
|
||||||
|
"ui.panel.page-onboarding.integration.all_set"
|
||||||
|
)}
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
${this.onboardingLocalize(
|
||||||
|
"ui.panel.page-onboarding.integration.lets_start"
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<div class="footer">
|
||||||
|
<ha-button unelevated @click=${this._finish}>
|
||||||
|
${this.onboardingLocalize(
|
||||||
|
"ui.panel.page-onboarding.integration.finish"
|
||||||
|
)}
|
||||||
|
</ha-button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
if (domains.length > 12) {
|
if (domains.length > 12) {
|
||||||
domains = domains.slice(0, 11);
|
domains = domains.slice(0, 11);
|
||||||
}
|
}
|
||||||
@ -149,11 +173,11 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<mwc-button unelevated @click=${this._finish}>
|
<ha-button unelevated @click=${this._finish}>
|
||||||
${this.onboardingLocalize(
|
${this.onboardingLocalize(
|
||||||
"ui.panel.page-onboarding.integration.finish"
|
"ui.panel.page-onboarding.integration.finish"
|
||||||
)}
|
)}
|
||||||
</mwc-button>
|
</ha-button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -193,6 +217,10 @@ class OnboardingIntegrations extends SubscribeMixin(LitElement) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.all-set-icon {
|
||||||
|
font-size: 64px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
mdiPencil,
|
mdiPencil,
|
||||||
mdiPlus,
|
mdiPlus,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
|
||||||
import {
|
import {
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
LitElement,
|
LitElement,
|
||||||
@ -15,15 +14,15 @@ import {
|
|||||||
html,
|
html,
|
||||||
nothing,
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { formatListWithAnds } from "../../../common/string/format-list";
|
import { formatListWithAnds } from "../../../common/string/format-list";
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
import "../../../components/ha-floor-icon";
|
import "../../../components/ha-floor-icon";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-svg-icon";
|
|
||||||
import "../../../components/ha-sortable";
|
import "../../../components/ha-sortable";
|
||||||
|
import "../../../components/ha-svg-icon";
|
||||||
import {
|
import {
|
||||||
AreaRegistryEntry,
|
AreaRegistryEntry,
|
||||||
createAreaRegistryEntry,
|
createAreaRegistryEntry,
|
||||||
@ -34,7 +33,6 @@ import {
|
|||||||
createFloorRegistryEntry,
|
createFloorRegistryEntry,
|
||||||
deleteFloorRegistryEntry,
|
deleteFloorRegistryEntry,
|
||||||
getFloorAreaLookup,
|
getFloorAreaLookup,
|
||||||
subscribeFloorRegistry,
|
|
||||||
updateFloorRegistryEntry,
|
updateFloorRegistryEntry,
|
||||||
} from "../../../data/floor_registry";
|
} from "../../../data/floor_registry";
|
||||||
import {
|
import {
|
||||||
@ -42,7 +40,6 @@ import {
|
|||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../dialogs/generic/show-dialog-box";
|
} from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
import "../ha-config-section";
|
import "../ha-config-section";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
@ -57,7 +54,7 @@ const UNASSIGNED_PATH = ["__unassigned__"];
|
|||||||
const SORT_OPTIONS = { sort: false, delay: 500, delayOnTouchOnly: true };
|
const SORT_OPTIONS = { sort: false, delay: 500, delayOnTouchOnly: true };
|
||||||
|
|
||||||
@customElement("ha-config-areas-dashboard")
|
@customElement("ha-config-areas-dashboard")
|
||||||
export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
export class HaConfigAreasDashboard extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ type: Boolean }) public isWide = false;
|
@property({ type: Boolean }) public isWide = false;
|
||||||
@ -66,14 +63,12 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
@state() private _floors?: FloorRegistryEntry[];
|
|
||||||
|
|
||||||
private _processAreas = memoizeOne(
|
private _processAreas = memoizeOne(
|
||||||
(
|
(
|
||||||
areas: HomeAssistant["areas"],
|
areas: HomeAssistant["areas"],
|
||||||
devices: HomeAssistant["devices"],
|
devices: HomeAssistant["devices"],
|
||||||
entities: HomeAssistant["entities"],
|
entities: HomeAssistant["entities"],
|
||||||
floors: FloorRegistryEntry[]
|
floors: HomeAssistant["floors"]
|
||||||
) => {
|
) => {
|
||||||
const processArea = (area: AreaRegistryEntry) => {
|
const processArea = (area: AreaRegistryEntry) => {
|
||||||
let noDevicesInArea = 0;
|
let noDevicesInArea = 0;
|
||||||
@ -109,7 +104,7 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||||||
(area) => !area.floor_id || !floorAreaLookup[area.floor_id]
|
(area) => !area.floor_id || !floorAreaLookup[area.floor_id]
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
floors: floors.map((floor) => ({
|
floors: Object.values(floors).map((floor) => ({
|
||||||
...floor,
|
...floor,
|
||||||
areas: (floorAreaLookup[floor.floor_id] || []).map(processArea),
|
areas: (floorAreaLookup[floor.floor_id] || []).map(processArea),
|
||||||
})),
|
})),
|
||||||
@ -118,26 +113,18 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
|
||||||
return [
|
|
||||||
subscribeFloorRegistry(this.hass.connection, (floors) => {
|
|
||||||
this._floors = floors;
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const areasAndFloors =
|
const areasAndFloors =
|
||||||
!this.hass.areas ||
|
!this.hass.areas ||
|
||||||
!this.hass.devices ||
|
!this.hass.devices ||
|
||||||
!this.hass.entities ||
|
!this.hass.entities ||
|
||||||
!this._floors
|
!this.hass.floors
|
||||||
? undefined
|
? undefined
|
||||||
: this._processAreas(
|
: this._processAreas(
|
||||||
this.hass.areas,
|
this.hass.areas,
|
||||||
this.hass.devices,
|
this.hass.devices,
|
||||||
this.hass.entities,
|
this.hass.entities,
|
||||||
this._floors
|
this.hass.floors
|
||||||
);
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -327,7 +314,7 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||||||
this.hass.areas,
|
this.hass.areas,
|
||||||
this.hass.devices,
|
this.hass.devices,
|
||||||
this.hass.entities,
|
this.hass.entities,
|
||||||
this._floors!
|
this.hass.floors
|
||||||
);
|
);
|
||||||
let area: AreaRegistryEntry;
|
let area: AreaRegistryEntry;
|
||||||
if (ev.detail.oldPath === UNASSIGNED_PATH) {
|
if (ev.detail.oldPath === UNASSIGNED_PATH) {
|
||||||
|
@ -28,8 +28,8 @@ import "../../../components/ha-domain-icon";
|
|||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-icon-button-prev";
|
import "../../../components/ha-icon-button-prev";
|
||||||
import "../../../components/ha-icon-next";
|
import "../../../components/ha-icon-next";
|
||||||
import "../../../components/ha-list-item-new";
|
import "../../../components/ha-md-list-item";
|
||||||
import "../../../components/ha-list-new";
|
import "../../../components/ha-md-list";
|
||||||
import "../../../components/ha-service-icon";
|
import "../../../components/ha-service-icon";
|
||||||
import "../../../components/search-input";
|
import "../../../components/search-input";
|
||||||
import {
|
import {
|
||||||
@ -434,7 +434,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
protected _opened(): void {
|
protected _opened(): void {
|
||||||
// Store the width and height so that when we search, box doesn't jump
|
// Store the width and height so that when we search, box doesn't jump
|
||||||
const boundingRect =
|
const boundingRect =
|
||||||
this.shadowRoot!.querySelector("ha-list-new")?.getBoundingClientRect();
|
this.shadowRoot!.querySelector("ha-md-list")?.getBoundingClientRect();
|
||||||
this._width = boundingRect?.width;
|
this._width = boundingRect?.width;
|
||||||
this._height = boundingRect?.height;
|
this._height = boundingRect?.height;
|
||||||
}
|
}
|
||||||
@ -526,7 +526,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
)}
|
)}
|
||||||
></search-input>
|
></search-input>
|
||||||
</div>
|
</div>
|
||||||
<ha-list-new
|
<ha-md-list
|
||||||
dialogInitialFocus=${ifDefined(this._fullScreen ? "" : undefined)}
|
dialogInitialFocus=${ifDefined(this._fullScreen ? "" : undefined)}
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
width: this._width ? `${this._width}px` : "auto",
|
width: this._width ? `${this._width}px` : "auto",
|
||||||
@ -537,7 +537,7 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
!this._filter &&
|
!this._filter &&
|
||||||
(!this._group ||
|
(!this._group ||
|
||||||
items.find((item) => item.key === this._params!.clipboardItem))
|
items.find((item) => item.key === this._params!.clipboardItem))
|
||||||
? html`<ha-list-item-new
|
? html`<ha-md-list-item
|
||||||
interactive
|
interactive
|
||||||
type="button"
|
type="button"
|
||||||
class="paste"
|
class="paste"
|
||||||
@ -558,14 +558,14 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
.path=${mdiContentPaste}
|
.path=${mdiContentPaste}
|
||||||
></ha-svg-icon
|
></ha-svg-icon
|
||||||
><ha-svg-icon slot="end" .path=${mdiPlus}></ha-svg-icon>
|
><ha-svg-icon slot="end" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>`
|
<md-divider role="separator" tabindex="-1"></md-divider>`
|
||||||
: ""}
|
: ""}
|
||||||
${repeat(
|
${repeat(
|
||||||
items,
|
items,
|
||||||
(item) => item.key,
|
(item) => item.key,
|
||||||
(item) => html`
|
(item) => html`
|
||||||
<ha-list-item-new
|
<ha-md-list-item
|
||||||
interactive
|
interactive
|
||||||
type="button"
|
type="button"
|
||||||
.value=${item.key}
|
.value=${item.key}
|
||||||
@ -588,10 +588,10 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiPlus}
|
.path=${mdiPlus}
|
||||||
></ha-svg-icon>`}
|
></ha-svg-icon>`}
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</ha-list-new>
|
</ha-md-list>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -643,13 +643,13 @@ class DialogAddAutomationElement extends LitElement implements HassDialog {
|
|||||||
ha-icon-next {
|
ha-icon-next {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
ha-list-new {
|
ha-md-list {
|
||||||
max-height: 468px;
|
max-height: 468px;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
--md-list-item-leading-space: 24px;
|
--md-list-item-leading-space: 24px;
|
||||||
--md-list-item-trailing-space: 24px;
|
--md-list-item-trailing-space: 24px;
|
||||||
}
|
}
|
||||||
ha-list-item-new img {
|
ha-md-list-item img {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
search-input {
|
search-input {
|
||||||
|
@ -6,8 +6,8 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import "../../../../components/ha-dialog-header";
|
import "../../../../components/ha-dialog-header";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
import "../../../../components/ha-list-item-new";
|
import "../../../../components/ha-md-list-item";
|
||||||
import "../../../../components/ha-list-new";
|
import "../../../../components/ha-md-list";
|
||||||
import "../../../../components/ha-radio";
|
import "../../../../components/ha-radio";
|
||||||
import "../../../../components/ha-textfield";
|
import "../../../../components/ha-textfield";
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ class DialogAutomationMode extends LitElement implements HassDialog {
|
|||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
</a>
|
</a>
|
||||||
</ha-dialog-header>
|
</ha-dialog-header>
|
||||||
<ha-list-new
|
<ha-md-list
|
||||||
role="listbox"
|
role="listbox"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-activedescendant="option-${this._newMode}"
|
aria-activedescendant="option-${this._newMode}"
|
||||||
@ -103,7 +103,7 @@ class DialogAutomationMode extends LitElement implements HassDialog {
|
|||||||
`ui.panel.config.automation.editor.modes.${mode}`
|
`ui.panel.config.automation.editor.modes.${mode}`
|
||||||
);
|
);
|
||||||
return html`
|
return html`
|
||||||
<ha-list-item-new
|
<ha-md-list-item
|
||||||
class="option"
|
class="option"
|
||||||
type="button"
|
type="button"
|
||||||
@click=${this._modeChanged}
|
@click=${this._modeChanged}
|
||||||
@ -132,10 +132,10 @@ class DialogAutomationMode extends LitElement implements HassDialog {
|
|||||||
`ui.panel.config.automation.editor.modes.${mode}_description`
|
`ui.panel.config.automation.editor.modes.${mode}_description`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</ha-list-new>
|
</ha-md-list>
|
||||||
|
|
||||||
${isMaxMode(this._newMode)
|
${isMaxMode(this._newMode)
|
||||||
? html`
|
? html`
|
||||||
|
@ -67,8 +67,8 @@ import "../../../components/ha-icon-button";
|
|||||||
import "../../../components/ha-icon-overflow-menu";
|
import "../../../components/ha-icon-overflow-menu";
|
||||||
import "../../../components/ha-menu";
|
import "../../../components/ha-menu";
|
||||||
import type { HaMenu } from "../../../components/ha-menu";
|
import type { HaMenu } from "../../../components/ha-menu";
|
||||||
import "../../../components/ha-menu-item";
|
import "../../../components/ha-md-menu-item";
|
||||||
import type { HaMenuItem } from "../../../components/ha-menu-item";
|
import type { HaMdMenuItem } from "../../../components/ha-md-menu-item";
|
||||||
import "../../../components/ha-sub-menu";
|
import "../../../components/ha-sub-menu";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||||
@ -403,7 +403,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const categoryItems = html`${this._categories?.map(
|
const categoryItems = html`${this._categories?.map(
|
||||||
(category) =>
|
(category) =>
|
||||||
html`<ha-menu-item
|
html`<ha-md-menu-item
|
||||||
.value=${category.category_id}
|
.value=${category.category_id}
|
||||||
@click=${this._handleBulkCategory}
|
@click=${this._handleBulkCategory}
|
||||||
>
|
>
|
||||||
@ -411,21 +411,21 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
|
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
|
||||||
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
|
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
|
||||||
<div slot="headline">${category.name}</div>
|
<div slot="headline">${category.name}</div>
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
)}
|
)}
|
||||||
<ha-menu-item .value=${null} @click=${this._handleBulkCategory}>
|
<ha-md-menu-item .value=${null} @click=${this._handleBulkCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.no_category"
|
"ui.panel.config.automation.picker.bulk_actions.no_category"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateCategory}>
|
<ha-md-menu-item @click=${this._bulkCreateCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
|
|
||||||
const labelItems = html`${this._labels?.map((label) => {
|
const labelItems = html`${this._labels?.map((label) => {
|
||||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||||
@ -437,7 +437,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
this._selected.some((entityId) =>
|
this._selected.some((entityId) =>
|
||||||
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
||||||
);
|
);
|
||||||
return html`<ha-menu-item
|
return html`<ha-md-menu-item
|
||||||
.value=${label.label_id}
|
.value=${label.label_id}
|
||||||
.action=${selected ? "remove" : "add"}
|
.action=${selected ? "remove" : "add"}
|
||||||
@click=${this._handleBulkLabel}
|
@click=${this._handleBulkLabel}
|
||||||
@ -455,18 +455,18 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
${label.name}
|
${label.name}
|
||||||
</ha-label>
|
</ha-label>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateLabel}>
|
<ha-md-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-md-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
|
||||||
const areaItems = html`${Object.values(this.hass.areas).map(
|
const areaItems = html`${Object.values(this.hass.areas).map(
|
||||||
(area) =>
|
(area) =>
|
||||||
html`<ha-menu-item
|
html`<ha-md-menu-item
|
||||||
.value=${area.area_id}
|
.value=${area.area_id}
|
||||||
@click=${this._handleBulkArea}
|
@click=${this._handleBulkArea}
|
||||||
>
|
>
|
||||||
@ -477,23 +477,23 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
.path=${mdiTextureBox}
|
.path=${mdiTextureBox}
|
||||||
></ha-svg-icon>`}
|
></ha-svg-icon>`}
|
||||||
<div slot="headline">${area.name}</div>
|
<div slot="headline">${area.name}</div>
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
)}
|
)}
|
||||||
<ha-menu-item .value=${null} @click=${this._handleBulkArea}>
|
<ha-md-menu-item .value=${null} @click=${this._handleBulkArea}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateArea}>
|
<ha-md-menu-item @click=${this._bulkCreateArea}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
|
|
||||||
const areasInOverflow =
|
const areasInOverflow =
|
||||||
(this._sizeController.value && this._sizeController.value < 900) ||
|
(this._sizeController.value && this._sizeController.value < 900) ||
|
||||||
@ -633,7 +633,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-filter-blueprints>
|
></ha-filter-blueprints>
|
||||||
${
|
${
|
||||||
!this.narrow
|
!this.narrow
|
||||||
? html`<ha-button-menu-new slot="selection-bar">
|
? html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -646,10 +646,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${categoryItems}
|
${categoryItems}
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
${labelsInOverflow
|
${labelsInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -662,10 +662,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>`}
|
</ha-md-button-menu>`}
|
||||||
${areasInOverflow
|
${areasInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -678,10 +678,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${areaItems}
|
${areaItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-md-button-menu>`}`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-md-button-menu has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-assist-chip
|
? html`<ha-assist-chip
|
||||||
@ -709,7 +709,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.move_category"
|
"ui.panel.config.automation.picker.bulk_actions.move_category"
|
||||||
@ -719,7 +719,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${categoryItems}</ha-menu>
|
<ha-menu slot="menu">${categoryItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
@ -727,7 +727,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow || labelsInOverflow
|
this.narrow || labelsInOverflow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
||||||
@ -737,7 +737,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${labelItems}</ha-menu>
|
<ha-menu slot="menu">${labelItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
@ -745,7 +745,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow || areasInOverflow
|
this.narrow || areasInOverflow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
@ -755,20 +755,20 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${areaItems}</ha-menu>
|
<ha-menu slot="menu">${areaItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
<ha-menu-item @click=${this._handleBulkEnable}>
|
<ha-md-menu-item @click=${this._handleBulkEnable}>
|
||||||
<ha-svg-icon slot="start" .path=${mdiToggleSwitch}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiToggleSwitch}></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.enable"
|
"ui.panel.config.automation.picker.bulk_actions.enable"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item @click=${this._handleBulkDisable}>
|
<ha-md-menu-item @click=${this._handleBulkDisable}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="start"
|
slot="start"
|
||||||
.path=${mdiToggleSwitchOffOutline}
|
.path=${mdiToggleSwitchOffOutline}
|
||||||
@ -778,8 +778,8 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.automation.picker.bulk_actions.disable"
|
"ui.panel.config.automation.picker.bulk_actions.disable"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
${
|
${
|
||||||
!this.automations.length
|
!this.automations.length
|
||||||
? html`<div class="empty" slot="empty">
|
? html`<div class="empty" slot="empty">
|
||||||
@ -827,7 +827,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
</ha-fab>
|
</ha-fab>
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
<ha-menu id="overflow-menu" positioning="fixed">
|
<ha-menu id="overflow-menu" positioning="fixed">
|
||||||
<ha-menu-item .clickAction=${this._showInfo}>
|
<ha-md-menu-item .clickAction=${this._showInfo}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
.path=${mdiInformationOutline}
|
.path=${mdiInformationOutline}
|
||||||
slot="start"
|
slot="start"
|
||||||
@ -835,46 +835,46 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
|
${this.hass.localize("ui.panel.config.automation.editor.show_info")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<ha-menu-item .clickAction=${this._showSettings}>
|
<ha-md-menu-item .clickAction=${this._showSettings}>
|
||||||
<ha-svg-icon .path=${mdiCog} slot="start"></ha-svg-icon>
|
<ha-svg-icon .path=${mdiCog} slot="start"></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.show_settings"
|
"ui.panel.config.automation.picker.show_settings"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item .clickAction=${this._editCategory}>
|
<ha-md-menu-item .clickAction=${this._editCategory}>
|
||||||
<ha-svg-icon .path=${mdiTag} slot="start"></ha-svg-icon>
|
<ha-svg-icon .path=${mdiTag} slot="start"></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`ui.panel.config.automation.picker.${this._overflowAutomation?.category ? "edit_category" : "assign_category"}`
|
`ui.panel.config.automation.picker.${this._overflowAutomation?.category ? "edit_category" : "assign_category"}`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item .clickAction=${this._runActions}>
|
<ha-md-menu-item .clickAction=${this._runActions}>
|
||||||
<ha-svg-icon .path=${mdiPlay} slot="start"></ha-svg-icon>
|
<ha-svg-icon .path=${mdiPlay} slot="start"></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.automation.editor.run")}
|
${this.hass.localize("ui.panel.config.automation.editor.run")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item .clickAction=${this._showTrace}>
|
<ha-md-menu-item .clickAction=${this._showTrace}>
|
||||||
<ha-svg-icon .path=${mdiTransitConnection} slot="start"></ha-svg-icon>
|
<ha-svg-icon .path=${mdiTransitConnection} slot="start"></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.show_trace"
|
"ui.panel.config.automation.editor.show_trace"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item .clickAction=${this._duplicate}>
|
<ha-md-menu-item .clickAction=${this._duplicate}>
|
||||||
<ha-svg-icon .path=${mdiContentDuplicate} slot="start"></ha-svg-icon>
|
<ha-svg-icon .path=${mdiContentDuplicate} slot="start"></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.automation.picker.duplicate")}
|
${this.hass.localize("ui.panel.config.automation.picker.duplicate")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item .clickAction=${this._toggle}>
|
<ha-md-menu-item .clickAction=${this._toggle}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
.path=${
|
.path=${
|
||||||
this._overflowAutomation?.state === "off"
|
this._overflowAutomation?.state === "off"
|
||||||
@ -892,13 +892,13 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item .clickAction=${this._deleteConfirm} class="warning">
|
<ha-md-menu-item .clickAction=${this._deleteConfirm} class="warning">
|
||||||
<ha-svg-icon .path=${mdiDelete} slot="start"></ha-svg-icon>
|
<ha-svg-icon .path=${mdiDelete} slot="start"></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.automation.picker.delete")}
|
${this.hass.localize("ui.panel.config.automation.picker.delete")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
</ha-menu>
|
</ha-menu>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -1056,13 +1056,13 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
this._applyFilters();
|
this._applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _showInfo = (item: HaMenuItem) => {
|
private _showInfo = (item: HaMdMenuItem) => {
|
||||||
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
||||||
.automation;
|
.automation;
|
||||||
fireEvent(this, "hass-more-info", { entityId: automation.entity_id });
|
fireEvent(this, "hass-more-info", { entityId: automation.entity_id });
|
||||||
};
|
};
|
||||||
|
|
||||||
private _showSettings = (item: HaMenuItem) => {
|
private _showSettings = (item: HaMdMenuItem) => {
|
||||||
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
||||||
.automation;
|
.automation;
|
||||||
|
|
||||||
@ -1072,14 +1072,14 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private _runActions = (item: HaMenuItem) => {
|
private _runActions = (item: HaMdMenuItem) => {
|
||||||
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
||||||
.automation;
|
.automation;
|
||||||
|
|
||||||
triggerAutomationActions(this.hass, automation.entity_id);
|
triggerAutomationActions(this.hass, automation.entity_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _editCategory = (item: HaMenuItem) => {
|
private _editCategory = (item: HaMdMenuItem) => {
|
||||||
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
||||||
.automation;
|
.automation;
|
||||||
|
|
||||||
@ -1103,7 +1103,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private _showTrace = (item: HaMenuItem) => {
|
private _showTrace = (item: HaMdMenuItem) => {
|
||||||
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
||||||
.automation;
|
.automation;
|
||||||
|
|
||||||
@ -1120,7 +1120,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private _toggle = async (item: HaMenuItem): Promise<void> => {
|
private _toggle = async (item: HaMdMenuItem): Promise<void> => {
|
||||||
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
||||||
.automation;
|
.automation;
|
||||||
|
|
||||||
@ -1130,7 +1130,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private _deleteConfirm = async (item: HaMenuItem) => {
|
private _deleteConfirm = async (item: HaMdMenuItem) => {
|
||||||
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
||||||
.automation;
|
.automation;
|
||||||
|
|
||||||
@ -1167,7 +1167,7 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _duplicate = async (item: HaMenuItem) => {
|
private _duplicate = async (item: HaMdMenuItem) => {
|
||||||
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
const automation = ((item.parentElement as HaMenu)!.anchorElement as any)!
|
||||||
.automation;
|
.automation;
|
||||||
|
|
||||||
@ -1455,7 +1455,7 @@ ${rejected
|
|||||||
ha-assist-chip {
|
ha-assist-chip {
|
||||||
--ha-assist-chip-container-shape: 10px;
|
--ha-assist-chip-container-shape: 10px;
|
||||||
}
|
}
|
||||||
ha-button-menu-new ha-assist-chip {
|
ha-md-button-menu ha-assist-chip {
|
||||||
--md-assist-chip-trailing-space: 8px;
|
--md-assist-chip-trailing-space: 8px;
|
||||||
}
|
}
|
||||||
ha-label {
|
ha-label {
|
||||||
|
@ -153,7 +153,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
.filter((entId) => entId in entryLookup)
|
.filter((entId) => entId in entryLookup)
|
||||||
.map((entry) => entryLookup[entry]);
|
.map((entry) => entryLookup[entry]);
|
||||||
|
|
||||||
return sortConfigEntries(deviceEntries, manifestLookup);
|
return sortConfigEntries(deviceEntries, device.primary_config_entry);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -189,20 +189,20 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
const result = groupBy(entities, (entry) => {
|
const result = groupBy(entities, (entry) => {
|
||||||
const domain = computeDomain(entry.entity_id);
|
const domain = computeDomain(entry.entity_id);
|
||||||
|
|
||||||
if (entry.entity_category) {
|
if (ASSIST_ENTITIES.includes(domain)) {
|
||||||
return entry.entity_category;
|
return "assist";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "event" || domain === "notify") {
|
if (domain === "event" || domain === "notify") {
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SENSOR_ENTITIES.includes(domain)) {
|
if (entry.entity_category) {
|
||||||
return "sensor";
|
return entry.entity_category;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ASSIST_ENTITIES.includes(domain)) {
|
if (SENSOR_ENTITIES.includes(domain)) {
|
||||||
return "assist";
|
return "sensor";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "control";
|
return "control";
|
||||||
@ -1536,6 +1536,10 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ha-card:has(ha-logbook) {
|
||||||
|
padding-bottom: var(--ha-card-border-radius, 12px);
|
||||||
|
}
|
||||||
|
|
||||||
ha-logbook {
|
ha-logbook {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ import "../../../components/ha-filter-integrations";
|
|||||||
import "../../../components/ha-filter-labels";
|
import "../../../components/ha-filter-labels";
|
||||||
import "../../../components/ha-filter-states";
|
import "../../../components/ha-filter-states";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-menu-item";
|
import "../../../components/ha-md-menu-item";
|
||||||
import "../../../components/ha-sub-menu";
|
import "../../../components/ha-sub-menu";
|
||||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||||
import { ConfigEntry, sortConfigEntries } from "../../../data/config_entries";
|
import { ConfigEntry, sortConfigEntries } from "../../../data/config_entries";
|
||||||
@ -388,7 +388,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
device.config_entries
|
device.config_entries
|
||||||
.filter((entId) => entId in entryLookup)
|
.filter((entId) => entId in entryLookup)
|
||||||
.map((entId) => entryLookup[entId]),
|
.map((entId) => entryLookup[entId]),
|
||||||
manifestLookup
|
device.primary_config_entry
|
||||||
);
|
);
|
||||||
|
|
||||||
const labels = labelReg && device?.labels;
|
const labels = labelReg && device?.labels;
|
||||||
@ -629,7 +629,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
const areaItems = html`${Object.values(this.hass.areas).map(
|
const areaItems = html`${Object.values(this.hass.areas).map(
|
||||||
(area) =>
|
(area) =>
|
||||||
html`<ha-menu-item
|
html`<ha-md-menu-item
|
||||||
.value=${area.area_id}
|
.value=${area.area_id}
|
||||||
@click=${this._handleBulkArea}
|
@click=${this._handleBulkArea}
|
||||||
>
|
>
|
||||||
@ -640,23 +640,23 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
.path=${mdiTextureBox}
|
.path=${mdiTextureBox}
|
||||||
></ha-svg-icon>`}
|
></ha-svg-icon>`}
|
||||||
<div slot="headline">${area.name}</div>
|
<div slot="headline">${area.name}</div>
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
)}
|
)}
|
||||||
<ha-menu-item .value=${null} @click=${this._handleBulkArea}>
|
<ha-md-menu-item .value=${null} @click=${this._handleBulkArea}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateArea}>
|
<ha-md-menu-item @click=${this._bulkCreateArea}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
|
|
||||||
const labelItems = html`${this._labels?.map((label) => {
|
const labelItems = html`${this._labels?.map((label) => {
|
||||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||||
@ -668,7 +668,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
this._selected.some((deviceId) =>
|
this._selected.some((deviceId) =>
|
||||||
this.hass.devices[deviceId]?.labels.includes(label.label_id)
|
this.hass.devices[deviceId]?.labels.includes(label.label_id)
|
||||||
);
|
);
|
||||||
return html`<ha-menu-item
|
return html`<ha-md-menu-item
|
||||||
.value=${label.label_id}
|
.value=${label.label_id}
|
||||||
.action=${selected ? "remove" : "add"}
|
.action=${selected ? "remove" : "add"}
|
||||||
@click=${this._handleBulkLabel}
|
@click=${this._handleBulkLabel}
|
||||||
@ -686,13 +686,13 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
${label.name}
|
${label.name}
|
||||||
</ha-label>
|
</ha-label>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateLabel}>
|
<ha-md-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-md-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -802,7 +802,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-filter-labels>
|
></ha-filter-labels>
|
||||||
|
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`<ha-button-menu-new slot="selection-bar">
|
? html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -815,11 +815,11 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
|
|
||||||
${areasInOverflow
|
${areasInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -832,10 +832,10 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${areaItems}
|
${areaItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-md-button-menu>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || areasInOverflow
|
${this.narrow || areasInOverflow
|
||||||
? html`<ha-button-menu-new has-overflow slot="selection-bar">
|
? html`<ha-md-button-menu has-overflow slot="selection-bar">
|
||||||
${this.narrow
|
${this.narrow
|
||||||
? html`<ha-assist-chip
|
? html`<ha-assist-chip
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -855,7 +855,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-icon-button>`}
|
></ha-icon-button>`}
|
||||||
${this.narrow
|
${this.narrow
|
||||||
? html` <ha-sub-menu>
|
? html` <ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
||||||
@ -865,12 +865,12 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${labelItems}</ha-menu>
|
<ha-menu slot="menu">${labelItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-sub-menu>
|
<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
@ -880,10 +880,10 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${areaItems}</ha-menu>
|
<ha-menu slot="menu">${areaItems}</ha-menu>
|
||||||
</ha-sub-menu>
|
</ha-sub-menu>
|
||||||
</ha-button-menu-new>`
|
</ha-md-button-menu>`
|
||||||
: nothing}
|
: nothing}
|
||||||
</hass-tabs-subpage-data-table>
|
</hass-tabs-subpage-data-table>
|
||||||
`;
|
`;
|
||||||
@ -1100,7 +1100,7 @@ ${rejected
|
|||||||
ha-assist-chip {
|
ha-assist-chip {
|
||||||
--ha-assist-chip-container-shape: 10px;
|
--ha-assist-chip-container-shape: 10px;
|
||||||
}
|
}
|
||||||
ha-button-menu-new ha-assist-chip {
|
ha-md-button-menu ha-assist-chip {
|
||||||
--md-assist-chip-trailing-space: 8px;
|
--md-assist-chip-trailing-space: 8px;
|
||||||
}
|
}
|
||||||
ha-label {
|
ha-label {
|
||||||
|
@ -60,7 +60,7 @@ import "../../../components/ha-filter-labels";
|
|||||||
import "../../../components/ha-filter-states";
|
import "../../../components/ha-filter-states";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-menu-item";
|
import "../../../components/ha-md-menu-item";
|
||||||
import "../../../components/ha-sub-menu";
|
import "../../../components/ha-sub-menu";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
||||||
@ -99,9 +99,8 @@ import "../integrations/ha-integration-overflow-menu";
|
|||||||
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
import { showAddIntegrationDialog } from "../integrations/show-add-integration-dialog";
|
||||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||||
import {
|
import {
|
||||||
serializeFilters,
|
DataTableFiltersValues,
|
||||||
deserializeFilters,
|
DataTableFiltersItems,
|
||||||
DataTableFilters,
|
|
||||||
} from "../../../data/data_table_filters";
|
} from "../../../data/data_table_filters";
|
||||||
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
||||||
|
|
||||||
@ -157,13 +156,13 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@storage({
|
@storage({
|
||||||
storage: "sessionStorage",
|
storage: "sessionStorage",
|
||||||
key: "entities-table-filters-full",
|
key: "entities-table-filters",
|
||||||
state: true,
|
state: true,
|
||||||
subscribe: false,
|
subscribe: false,
|
||||||
serializer: serializeFilters,
|
|
||||||
deserializer: deserializeFilters,
|
|
||||||
})
|
})
|
||||||
private _filters: DataTableFilters = {};
|
private _filters: DataTableFiltersValues = {};
|
||||||
|
|
||||||
|
@state() private _filteredItems: DataTableFiltersItems = {};
|
||||||
|
|
||||||
@state() private _selected: string[] = [];
|
@state() private _selected: string[] = [];
|
||||||
|
|
||||||
@ -460,13 +459,14 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
devices: HomeAssistant["devices"],
|
devices: HomeAssistant["devices"],
|
||||||
areas: HomeAssistant["areas"],
|
areas: HomeAssistant["areas"],
|
||||||
stateEntities: StateEntity[],
|
stateEntities: StateEntity[],
|
||||||
filters: DataTableFilters,
|
filters: DataTableFiltersValues,
|
||||||
|
filteredItems: DataTableFiltersItems,
|
||||||
entries?: ConfigEntry[],
|
entries?: ConfigEntry[],
|
||||||
labelReg?: LabelRegistryEntry[]
|
labelReg?: LabelRegistryEntry[]
|
||||||
) => {
|
) => {
|
||||||
const result: EntityRow[] = [];
|
const result: EntityRow[] = [];
|
||||||
|
|
||||||
const stateFilters = filters["ha-filter-states"]?.value as string[];
|
const stateFilters = filters["ha-filter-states"] as string[];
|
||||||
|
|
||||||
const showEnabled =
|
const showEnabled =
|
||||||
!stateFilters?.length || stateFilters.includes("enabled");
|
!stateFilters?.length || stateFilters.includes("enabled");
|
||||||
@ -491,15 +491,11 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
const filteredDomains = new Set<string>();
|
const filteredDomains = new Set<string>();
|
||||||
|
|
||||||
Object.entries(filters).forEach(([key, filter]) => {
|
Object.entries(filters).forEach(([key, filter]) => {
|
||||||
if (
|
if (key === "config_entry" && Array.isArray(filter) && filter.length) {
|
||||||
key === "config_entry" &&
|
|
||||||
Array.isArray(filter.value) &&
|
|
||||||
filter.value.length
|
|
||||||
) {
|
|
||||||
filteredEntities = filteredEntities.filter(
|
filteredEntities = filteredEntities.filter(
|
||||||
(entity) =>
|
(entity) =>
|
||||||
entity.config_entry_id &&
|
entity.config_entry_id &&
|
||||||
(filter.value as string[]).includes(entity.config_entry_id)
|
(filter as string[]).includes(entity.config_entry_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!entries) {
|
if (!entries) {
|
||||||
@ -509,8 +505,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
const configEntries = entries.filter(
|
const configEntries = entries.filter(
|
||||||
(entry) =>
|
(entry) =>
|
||||||
entry.entry_id &&
|
entry.entry_id && (filter as string[]).includes(entry.entry_id)
|
||||||
(filter.value as string[]).includes(entry.entry_id)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
configEntries.forEach((configEntry) => {
|
configEntries.forEach((configEntry) => {
|
||||||
@ -521,17 +516,15 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
key === "ha-filter-integrations" &&
|
key === "ha-filter-integrations" &&
|
||||||
Array.isArray(filter.value) &&
|
Array.isArray(filter) &&
|
||||||
filter.value.length
|
filter.length
|
||||||
) {
|
) {
|
||||||
if (!entries) {
|
if (!entries) {
|
||||||
this._loadConfigEntries();
|
this._loadConfigEntries();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const entryIds = entries
|
const entryIds = entries
|
||||||
.filter((entry) =>
|
.filter((entry) => (filter as string[]).includes(entry.domain))
|
||||||
(filter.value as string[]).includes(entry.domain)
|
|
||||||
)
|
|
||||||
.map((entry) => entry.entry_id);
|
.map((entry) => entry.entry_id);
|
||||||
|
|
||||||
const filteredEntitiesByDomain = new Set<string>();
|
const filteredEntitiesByDomain = new Set<string>();
|
||||||
@ -547,7 +540,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
entitiesByDomain[source.domain].push(entity);
|
entitiesByDomain[source.domain].push(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const val of filter.value) {
|
for (const val of filter) {
|
||||||
if (val in entitiesByDomain) {
|
if (val in entitiesByDomain) {
|
||||||
entitiesByDomain[val].forEach((item) =>
|
entitiesByDomain[val].forEach((item) =>
|
||||||
filteredEntitiesByDomain.add(item)
|
filteredEntitiesByDomain.add(item)
|
||||||
@ -558,32 +551,34 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
filteredEntities = filteredEntities.filter(
|
filteredEntities = filteredEntities.filter(
|
||||||
(entity) =>
|
(entity) =>
|
||||||
filteredEntitiesByDomain.has(entity.entity_id) ||
|
filteredEntitiesByDomain.has(entity.entity_id) ||
|
||||||
(filter.value as string[]).includes(entity.platform) ||
|
(filter as string[]).includes(entity.platform) ||
|
||||||
(entity.config_entry_id &&
|
(entity.config_entry_id &&
|
||||||
entryIds.includes(entity.config_entry_id))
|
entryIds.includes(entity.config_entry_id))
|
||||||
);
|
);
|
||||||
filter.value!.forEach((domain) => filteredDomains.add(domain));
|
filter!.forEach((domain) => filteredDomains.add(domain));
|
||||||
} else if (
|
} else if (
|
||||||
key === "ha-filter-domains" &&
|
key === "ha-filter-domains" &&
|
||||||
Array.isArray(filter.value) &&
|
Array.isArray(filter) &&
|
||||||
filter.value.length
|
filter.length
|
||||||
) {
|
) {
|
||||||
filteredEntities = filteredEntities.filter((entity) =>
|
filteredEntities = filteredEntities.filter((entity) =>
|
||||||
(filter.value as string[]).includes(computeDomain(entity.entity_id))
|
(filter as string[]).includes(computeDomain(entity.entity_id))
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
key === "ha-filter-labels" &&
|
key === "ha-filter-labels" &&
|
||||||
Array.isArray(filter.value) &&
|
Array.isArray(filter) &&
|
||||||
filter.value.length
|
filter.length
|
||||||
) {
|
) {
|
||||||
filteredEntities = filteredEntities.filter((entity) =>
|
filteredEntities = filteredEntities.filter((entity) =>
|
||||||
entity.labels.some((lbl) =>
|
entity.labels.some((lbl) => (filter as string[]).includes(lbl))
|
||||||
(filter.value as string[]).includes(lbl)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} else if (filter.items) {
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.values(filteredItems).forEach((items) => {
|
||||||
|
if (items) {
|
||||||
filteredEntities = filteredEntities.filter((entity) =>
|
filteredEntities = filteredEntities.filter((entity) =>
|
||||||
filter.items!.has(entity.entity_id)
|
items.has(entity.entity_id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -684,6 +679,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
this.hass.areas,
|
this.hass.areas,
|
||||||
this._stateEntities,
|
this._stateEntities,
|
||||||
this._filters,
|
this._filters,
|
||||||
|
this._filteredItems,
|
||||||
this._entries,
|
this._entries,
|
||||||
this._labels
|
this._labels
|
||||||
);
|
);
|
||||||
@ -704,7 +700,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
this._selected.some((entityId) =>
|
this._selected.some((entityId) =>
|
||||||
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
||||||
);
|
);
|
||||||
return html`<ha-menu-item
|
return html`<ha-md-menu-item
|
||||||
.value=${label.label_id}
|
.value=${label.label_id}
|
||||||
.action=${selected ? "remove" : "add"}
|
.action=${selected ? "remove" : "add"}
|
||||||
@click=${this._handleBulkLabel}
|
@click=${this._handleBulkLabel}
|
||||||
@ -722,13 +718,13 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
${label.name}
|
${label.name}
|
||||||
</ha-label>
|
</ha-label>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateLabel}>
|
<ha-md-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-md-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -749,10 +745,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
hasFilters
|
hasFilters
|
||||||
.filters=${
|
.filters=${
|
||||||
Object.values(this._filters).filter((filter) =>
|
Object.values(this._filters).filter((filter) =>
|
||||||
Array.isArray(filter.value)
|
Array.isArray(filter)
|
||||||
? filter.value.length
|
? filter.length
|
||||||
: filter.value &&
|
: filter &&
|
||||||
Object.values(filter.value).some((val) =>
|
Object.values(filter).some((val) =>
|
||||||
Array.isArray(val) ? val.length : val
|
Array.isArray(val) ? val.length : val
|
||||||
)
|
)
|
||||||
).length
|
).length
|
||||||
@ -786,7 +782,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
${
|
${
|
||||||
!this.narrow
|
!this.narrow
|
||||||
? html`<ha-button-menu-new slot="selection-bar">
|
? html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -796,10 +792,10 @@ ${
|
|||||||
<ha-svg-icon slot="trailing-icon" .path=${mdiMenuDown}></ha-svg-icon>
|
<ha-svg-icon slot="trailing-icon" .path=${mdiMenuDown}></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>`
|
</ha-md-button-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-md-button-menu has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-assist-chip
|
? html`<ha-assist-chip
|
||||||
@ -824,29 +820,29 @@ ${
|
|||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ha-svg-icon slot="end" .path=${mdiChevronRight}></ha-svg-icon>
|
<ha-svg-icon slot="end" .path=${mdiChevronRight}></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${labelItems}</ha-menu>
|
<ha-menu slot="menu">${labelItems}</ha-menu>
|
||||||
</ha-sub-menu>
|
</ha-sub-menu>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>`
|
<md-divider role="separator" tabindex="-1"></md-divider>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
<ha-menu-item @click=${this._enableSelected}>
|
<ha-md-menu-item @click=${this._enableSelected}>
|
||||||
<ha-svg-icon slot="start" .path=${mdiToggleSwitch}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiToggleSwitch}></ha-svg-icon>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.entities.picker.enable_selected.button"
|
"ui.panel.config.entities.picker.enable_selected.button"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item @click=${this._disableSelected}>
|
<ha-md-menu-item @click=${this._disableSelected}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="start"
|
slot="start"
|
||||||
.path=${mdiToggleSwitchOffOutline}
|
.path=${mdiToggleSwitchOffOutline}
|
||||||
@ -856,10 +852,10 @@ ${
|
|||||||
"ui.panel.config.entities.picker.disable_selected.button"
|
"ui.panel.config.entities.picker.disable_selected.button"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
|
|
||||||
<ha-menu-item @click=${this._unhideSelected}>
|
<ha-md-menu-item @click=${this._unhideSelected}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="start"
|
slot="start"
|
||||||
.path=${mdiEye}
|
.path=${mdiEye}
|
||||||
@ -869,8 +865,8 @@ ${
|
|||||||
"ui.panel.config.entities.picker.unhide_selected.button"
|
"ui.panel.config.entities.picker.unhide_selected.button"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu-item @click=${this._hideSelected}>
|
<ha-md-menu-item @click=${this._hideSelected}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="start"
|
slot="start"
|
||||||
.path=${mdiEyeOff}
|
.path=${mdiEyeOff}
|
||||||
@ -880,10 +876,10 @@ ${
|
|||||||
"ui.panel.config.entities.picker.hide_selected.button"
|
"ui.panel.config.entities.picker.hide_selected.button"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
|
|
||||||
<ha-menu-item @click=${this._removeSelected} class="warning">
|
<ha-md-menu-item @click=${this._removeSelected} class="warning">
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="start"
|
slot="start"
|
||||||
.path=${mdiDelete}
|
.path=${mdiDelete}
|
||||||
@ -893,25 +889,24 @@ ${
|
|||||||
"ui.panel.config.entities.picker.delete_selected.button"
|
"ui.panel.config.entities.picker.delete_selected.button"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
${
|
${
|
||||||
Array.isArray(this._filters.config_entry?.value) &&
|
Array.isArray(this._filters.config_entry) &&
|
||||||
this._filters.config_entry?.value.length
|
this._filters.config_entry?.length
|
||||||
? html`<ha-alert slot="filter-pane">
|
? html`<ha-alert slot="filter-pane">
|
||||||
Filtering by config entry
|
Filtering by config entry
|
||||||
${this._entries?.find(
|
${this._entries?.find(
|
||||||
(entry) =>
|
(entry) => entry.entry_id === this._filters.config_entry![0]
|
||||||
entry.entry_id === this._filters.config_entry!.value![0]
|
)?.title || this._filters.config_entry[0]}
|
||||||
)?.title || this._filters.config_entry.value[0]}
|
|
||||||
</ha-alert>`
|
</ha-alert>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
<ha-filter-floor-areas
|
<ha-filter-floor-areas
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
type="entity"
|
type="entity"
|
||||||
.value=${this._filters["ha-filter-floor-areas"]?.value}
|
.value=${this._filters["ha-filter-floor-areas"]}
|
||||||
@data-table-filter-changed=${this._filterChanged}
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
slot="filter-pane"
|
slot="filter-pane"
|
||||||
.expanded=${this._expandedFilter === "ha-filter-floor-areas"}
|
.expanded=${this._expandedFilter === "ha-filter-floor-areas"}
|
||||||
@ -921,7 +916,7 @@ ${
|
|||||||
<ha-filter-devices
|
<ha-filter-devices
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.type=${"entity"}
|
.type=${"entity"}
|
||||||
.value=${this._filters["ha-filter-devices"]?.value}
|
.value=${this._filters["ha-filter-devices"]}
|
||||||
@data-table-filter-changed=${this._filterChanged}
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
slot="filter-pane"
|
slot="filter-pane"
|
||||||
.expanded=${this._expandedFilter === "ha-filter-devices"}
|
.expanded=${this._expandedFilter === "ha-filter-devices"}
|
||||||
@ -930,7 +925,7 @@ ${
|
|||||||
></ha-filter-devices>
|
></ha-filter-devices>
|
||||||
<ha-filter-domains
|
<ha-filter-domains
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._filters["ha-filter-domains"]?.value}
|
.value=${this._filters["ha-filter-domains"]}
|
||||||
@data-table-filter-changed=${this._filterChanged}
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
slot="filter-pane"
|
slot="filter-pane"
|
||||||
.expanded=${this._expandedFilter === "ha-filter-domains"}
|
.expanded=${this._expandedFilter === "ha-filter-domains"}
|
||||||
@ -939,7 +934,7 @@ ${
|
|||||||
></ha-filter-domains>
|
></ha-filter-domains>
|
||||||
<ha-filter-integrations
|
<ha-filter-integrations
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._filters["ha-filter-integrations"]?.value}
|
.value=${this._filters["ha-filter-integrations"]}
|
||||||
@data-table-filter-changed=${this._filterChanged}
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
slot="filter-pane"
|
slot="filter-pane"
|
||||||
.expanded=${this._expandedFilter === "ha-filter-integrations"}
|
.expanded=${this._expandedFilter === "ha-filter-integrations"}
|
||||||
@ -951,7 +946,7 @@ ${
|
|||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.panel.config.entities.picker.headers.status"
|
"ui.panel.config.entities.picker.headers.status"
|
||||||
)}
|
)}
|
||||||
.value=${this._filters["ha-filter-states"]?.value}
|
.value=${this._filters["ha-filter-states"]}
|
||||||
.states=${this._states(this.hass.localize)}
|
.states=${this._states(this.hass.localize)}
|
||||||
@data-table-filter-changed=${this._filterChanged}
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
slot="filter-pane"
|
slot="filter-pane"
|
||||||
@ -961,7 +956,7 @@ ${
|
|||||||
></ha-filter-states>
|
></ha-filter-states>
|
||||||
<ha-filter-labels
|
<ha-filter-labels
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._filters["ha-filter-labels"]?.value}
|
.value=${this._filters["ha-filter-labels"]}
|
||||||
@data-table-filter-changed=${this._filterChanged}
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
slot="filter-pane"
|
slot="filter-pane"
|
||||||
.expanded=${this._expandedFilter === "ha-filter-labels"}
|
.expanded=${this._expandedFilter === "ha-filter-labels"}
|
||||||
@ -996,7 +991,9 @@ ${
|
|||||||
|
|
||||||
private _filterChanged(ev) {
|
private _filterChanged(ev) {
|
||||||
const type = ev.target.localName;
|
const type = ev.target.localName;
|
||||||
this._filters = { ...this._filters, [type]: ev.detail };
|
|
||||||
|
this._filters = { ...this._filters, [type]: ev.detail.value };
|
||||||
|
this._filteredItems = { ...this._filteredItems, [type]: ev.detail.items };
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
@ -1008,10 +1005,7 @@ ${
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._filters = {
|
this._filters = {
|
||||||
"ha-filter-states": {
|
"ha-filter-states": ["enabled"],
|
||||||
value: ["enabled"],
|
|
||||||
items: undefined,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1026,18 +1020,9 @@ ${
|
|||||||
this._filter = history.state?.filter || "";
|
this._filter = history.state?.filter || "";
|
||||||
|
|
||||||
this._filters = {
|
this._filters = {
|
||||||
"ha-filter-states": {
|
"ha-filter-states": [],
|
||||||
value: [],
|
"ha-filter-integrations": domain ? [domain] : [],
|
||||||
items: undefined,
|
config_entry: configEntry ? [configEntry] : [],
|
||||||
},
|
|
||||||
"ha-filter-integrations": {
|
|
||||||
value: domain ? [domain] : [],
|
|
||||||
items: undefined,
|
|
||||||
},
|
|
||||||
config_entry: {
|
|
||||||
value: configEntry ? [configEntry] : [],
|
|
||||||
items: undefined,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this._searchParms.has("label")) {
|
if (this._searchParms.has("label")) {
|
||||||
@ -1052,15 +1037,13 @@ ${
|
|||||||
}
|
}
|
||||||
this._filters = {
|
this._filters = {
|
||||||
...this._filters,
|
...this._filters,
|
||||||
"ha-filter-labels": {
|
"ha-filter-labels": [label],
|
||||||
value: [label],
|
|
||||||
items: undefined,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _clearFilter() {
|
private _clearFilter() {
|
||||||
this._filters = {};
|
this._filters = {};
|
||||||
|
this._filteredItems = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues): void {
|
public willUpdate(changedProps: PropertyValues): void {
|
||||||
@ -1070,8 +1053,10 @@ ${
|
|||||||
if (!this.hass || !this._entities) {
|
if (!this.hass || !this._entities) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
changedProps.has("hass") ||
|
(changedProps.has("hass") &&
|
||||||
|
(!oldHass || oldHass.states !== this.hass.states)) ||
|
||||||
changedProps.has("_entities") ||
|
changedProps.has("_entities") ||
|
||||||
changedProps.has("_entitySources")
|
changedProps.has("_entitySources")
|
||||||
) {
|
) {
|
||||||
@ -1084,9 +1069,9 @@ ${
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!oldHass ||
|
|
||||||
changedProps.has("_entitySources") ||
|
changedProps.has("_entitySources") ||
|
||||||
this.hass.states[entityId] !== oldHass.states[entityId]
|
(changedProps.has("hass") && !oldHass) ||
|
||||||
|
!oldHass.states[entityId]
|
||||||
) {
|
) {
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@ -1357,6 +1342,7 @@ ${rejected
|
|||||||
this.hass.areas,
|
this.hass.areas,
|
||||||
this._stateEntities,
|
this._stateEntities,
|
||||||
this._filters,
|
this._filters,
|
||||||
|
this._filteredItems,
|
||||||
this._entries,
|
this._entries,
|
||||||
this._labels
|
this._labels
|
||||||
);
|
);
|
||||||
@ -1468,7 +1454,7 @@ ${rejected
|
|||||||
ha-assist-chip {
|
ha-assist-chip {
|
||||||
--ha-assist-chip-container-shape: 10px;
|
--ha-assist-chip-container-shape: 10px;
|
||||||
}
|
}
|
||||||
ha-button-menu-new ha-assist-chip {
|
ha-md-button-menu ha-assist-chip {
|
||||||
--md-assist-chip-trailing-space: 8px;
|
--md-assist-chip-trailing-space: 8px;
|
||||||
}
|
}
|
||||||
ha-label {
|
ha-label {
|
||||||
|
@ -35,22 +35,17 @@ import { customElement, property, state } from "lit/decorators";
|
|||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { listenMediaQuery } from "../../common/dom/media_query";
|
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||||
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
||||||
import {
|
import { fullEntitiesContext, labelsContext } from "../../data/context";
|
||||||
floorsContext,
|
|
||||||
fullEntitiesContext,
|
|
||||||
labelsContext,
|
|
||||||
} from "../../data/context";
|
|
||||||
import {
|
import {
|
||||||
entityRegistryByEntityId,
|
entityRegistryByEntityId,
|
||||||
entityRegistryById,
|
entityRegistryById,
|
||||||
subscribeEntityRegistry,
|
subscribeEntityRegistry,
|
||||||
} from "../../data/entity_registry";
|
} from "../../data/entity_registry";
|
||||||
|
import { subscribeLabelRegistry } from "../../data/label_registry";
|
||||||
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||||
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
import { PageNavigation } from "../../layouts/hass-tabs-subpage";
|
||||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
import { HomeAssistant, Route } from "../../types";
|
import { HomeAssistant, Route } from "../../types";
|
||||||
import { subscribeLabelRegistry } from "../../data/label_registry";
|
|
||||||
import { subscribeFloorRegistry } from "../../data/floor_registry";
|
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -390,11 +385,6 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
|
|||||||
initialValue: [],
|
initialValue: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
private _floorsContext = new ContextProvider(this, {
|
|
||||||
context: floorsContext,
|
|
||||||
initialValue: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
return [
|
return [
|
||||||
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
@ -403,9 +393,6 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
|
|||||||
subscribeLabelRegistry(this.hass.connection!, (labels) => {
|
subscribeLabelRegistry(this.hass.connection!, (labels) => {
|
||||||
this._labelsContext.setValue(labels);
|
this._labelsContext.setValue(labels);
|
||||||
}),
|
}),
|
||||||
subscribeFloorRegistry(this.hass.connection!, (floors) => {
|
|
||||||
this._floorsContext.setValue(floors);
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,7 +479,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
const categoryItems = html`${this._categories?.map(
|
const categoryItems = html`${this._categories?.map(
|
||||||
(category) =>
|
(category) =>
|
||||||
html`<ha-menu-item
|
html`<ha-md-menu-item
|
||||||
.value=${category.category_id}
|
.value=${category.category_id}
|
||||||
@click=${this._handleBulkCategory}
|
@click=${this._handleBulkCategory}
|
||||||
>
|
>
|
||||||
@ -487,21 +487,21 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
|
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
|
||||||
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
|
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
|
||||||
<div slot="headline">${category.name}</div>
|
<div slot="headline">${category.name}</div>
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
)}
|
)}
|
||||||
<ha-menu-item .value=${null} @click=${this._handleBulkCategory}>
|
<ha-md-menu-item .value=${null} @click=${this._handleBulkCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.no_category"
|
"ui.panel.config.automation.picker.bulk_actions.no_category"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateCategory}>
|
<ha-md-menu-item @click=${this._bulkCreateCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
const labelItems = html`${this._labels?.map((label) => {
|
const labelItems = html`${this._labels?.map((label) => {
|
||||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||||
const selected = this._selected.every((entityId) =>
|
const selected = this._selected.every((entityId) =>
|
||||||
@ -512,7 +512,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
this._selected.some((entityId) =>
|
this._selected.some((entityId) =>
|
||||||
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
||||||
);
|
);
|
||||||
return html`<ha-menu-item
|
return html`<ha-md-menu-item
|
||||||
.value=${label.label_id}
|
.value=${label.label_id}
|
||||||
.action=${selected ? "remove" : "add"}
|
.action=${selected ? "remove" : "add"}
|
||||||
@click=${this._handleBulkLabel}
|
@click=${this._handleBulkLabel}
|
||||||
@ -530,13 +530,13 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
${label.name}
|
${label.name}
|
||||||
</ha-label>
|
</ha-label>
|
||||||
</ha-menu-item> `;
|
</ha-md-menu-item> `;
|
||||||
})}<md-divider role="separator" tabindex="-1"></md-divider>
|
})}<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateLabel}>
|
<ha-md-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
const labelsInOverflow =
|
const labelsInOverflow =
|
||||||
(this._sizeController.value && this._sizeController.value < 700) ||
|
(this._sizeController.value && this._sizeController.value < 700) ||
|
||||||
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
(!this._sizeController.value && this.hass.dockedSidebar === "docked");
|
||||||
@ -637,7 +637,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
></ha-filter-categories>
|
></ha-filter-categories>
|
||||||
|
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`<ha-button-menu-new slot="selection-bar">
|
? html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -650,10 +650,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${categoryItems}
|
${categoryItems}
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
${labelsInOverflow
|
${labelsInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -666,11 +666,11 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-md-button-menu>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || labelsInOverflow
|
${this.narrow || labelsInOverflow
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-md-button-menu has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-assist-chip
|
? html`<ha-assist-chip
|
||||||
@ -698,7 +698,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.move_category"
|
"ui.panel.config.automation.picker.bulk_actions.move_category"
|
||||||
@ -708,7 +708,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${categoryItems}</ha-menu>
|
<ha-menu slot="menu">${categoryItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
@ -716,7 +716,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow || this.hass.dockedSidebar === "docked"
|
this.narrow || this.hass.dockedSidebar === "docked"
|
||||||
? html` <ha-sub-menu>
|
? html` <ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
||||||
@ -726,12 +726,12 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${labelItems}</ha-menu>
|
<ha-menu slot="menu">${labelItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
</ha-button-menu-new>`
|
</ha-md-button-menu>`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-integration-overflow-menu
|
<ha-integration-overflow-menu
|
||||||
@ -1155,7 +1155,7 @@ ${rejected
|
|||||||
ha-assist-chip {
|
ha-assist-chip {
|
||||||
--ha-assist-chip-container-shape: 10px;
|
--ha-assist-chip-container-shape: 10px;
|
||||||
}
|
}
|
||||||
ha-button-menu-new ha-assist-chip {
|
ha-md-button-menu ha-assist-chip {
|
||||||
--md-assist-chip-trailing-space: 8px;
|
--md-assist-chip-trailing-space: 8px;
|
||||||
}
|
}
|
||||||
ha-label {
|
ha-label {
|
||||||
|
@ -45,12 +45,12 @@ import { isDevVersion } from "../../../common/config/version";
|
|||||||
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
import { caseInsensitiveStringCompare } from "../../../common/string/compare";
|
||||||
import { nextRender } from "../../../common/util/render-status";
|
import { nextRender } from "../../../common/util/render-status";
|
||||||
import "../../../components/ha-button";
|
import "../../../components/ha-button";
|
||||||
import "../../../components/ha-button-menu-new";
|
import "../../../components/ha-md-button-menu";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-list-item";
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-list-item-new";
|
import "../../../components/ha-md-list-item";
|
||||||
import "../../../components/ha-list-new";
|
import "../../../components/ha-md-list";
|
||||||
import "../../../components/ha-menu-item";
|
import "../../../components/ha-md-menu-item";
|
||||||
import {
|
import {
|
||||||
deleteApplicationCredential,
|
deleteApplicationCredential,
|
||||||
fetchApplicationCredentialsConfigEntry,
|
fetchApplicationCredentialsConfigEntry,
|
||||||
@ -474,10 +474,10 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.integrations.discovered"
|
"ui.panel.config.integrations.discovered"
|
||||||
)}
|
)}
|
||||||
</h1>
|
</h1>
|
||||||
<ha-list-new>
|
<ha-md-list>
|
||||||
${discoveryFlows.map(
|
${discoveryFlows.map(
|
||||||
(flow) =>
|
(flow) =>
|
||||||
html`<ha-list-item-new class="discovered">
|
html`<ha-md-list-item class="discovered">
|
||||||
${flow.localized_title}
|
${flow.localized_title}
|
||||||
<ha-button
|
<ha-button
|
||||||
slot="end"
|
slot="end"
|
||||||
@ -488,9 +488,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.integrations.configure"
|
"ui.panel.config.integrations.configure"
|
||||||
)}
|
)}
|
||||||
></ha-button>
|
></ha-button>
|
||||||
</ha-list-item-new>`
|
</ha-md-list-item>`
|
||||||
)}
|
)}
|
||||||
</ha-list-new>
|
</ha-md-list>
|
||||||
</ha-card>`
|
</ha-card>`
|
||||||
: ""}
|
: ""}
|
||||||
${attentionFlows.length || attentionEntries.length
|
${attentionFlows.length || attentionEntries.length
|
||||||
@ -500,12 +500,12 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
`ui.panel.config.integrations.integration_page.attention_entries`
|
`ui.panel.config.integrations.integration_page.attention_entries`
|
||||||
)}
|
)}
|
||||||
</h1>
|
</h1>
|
||||||
<ha-list-new>
|
<ha-md-list>
|
||||||
${attentionFlows.map((flow) => {
|
${attentionFlows.map((flow) => {
|
||||||
const attention = ATTENTION_SOURCES.includes(
|
const attention = ATTENTION_SOURCES.includes(
|
||||||
flow.context.source
|
flow.context.source
|
||||||
);
|
);
|
||||||
return html` <ha-list-item-new
|
return html` <ha-md-list-item
|
||||||
class="config_entry ${attention ? "attention" : ""}"
|
class="config_entry ${attention ? "attention" : ""}"
|
||||||
>
|
>
|
||||||
${flow.localized_title}
|
${flow.localized_title}
|
||||||
@ -527,7 +527,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
}`
|
}`
|
||||||
)}
|
)}
|
||||||
></ha-button>
|
></ha-button>
|
||||||
</ha-list-item-new>`;
|
</ha-md-list-item>`;
|
||||||
})}
|
})}
|
||||||
${attentionEntries.map(
|
${attentionEntries.map(
|
||||||
(item, index) =>
|
(item, index) =>
|
||||||
@ -539,7 +539,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
></md-divider>`
|
></md-divider>`
|
||||||
: ""} `
|
: ""} `
|
||||||
)}
|
)}
|
||||||
</ha-list-new>
|
</ha-md-list>
|
||||||
</ha-card>`
|
</ha-card>`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
@ -568,7 +568,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
)}
|
)}
|
||||||
</div>`
|
</div>`
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-list-new>
|
<ha-md-list>
|
||||||
${normalEntries.map(
|
${normalEntries.map(
|
||||||
(item, index) =>
|
(item, index) =>
|
||||||
html`${this._renderConfigEntry(item)}
|
html`${this._renderConfigEntry(item)}
|
||||||
@ -579,7 +579,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
></md-divider>`
|
></md-divider>`
|
||||||
: ""} `
|
: ""} `
|
||||||
)}
|
)}
|
||||||
</ha-list-new>
|
</ha-md-list>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-button @click=${this._addIntegration}>
|
<ha-button @click=${this._addIntegration}>
|
||||||
${this._manifest?.integration_type
|
${this._manifest?.integration_type
|
||||||
@ -743,7 +743,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
const configPanel = this._configPanel(item.domain, this.hass.panels);
|
const configPanel = this._configPanel(item.domain, this.hass.panels);
|
||||||
|
|
||||||
return html`<ha-list-item-new
|
return html`<ha-md-list-item
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
config_entry: true,
|
config_entry: true,
|
||||||
"state-not-loaded": item!.state === "not_loaded",
|
"state-not-loaded": item!.state === "not_loaded",
|
||||||
@ -797,23 +797,23 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-button-menu-new positioning="popover" slot="end">
|
<ha-md-button-menu positioning="popover" slot="end">
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
${item.supports_options && stateText
|
${item.supports_options && stateText
|
||||||
? html`<ha-menu-item @click=${this._showOptions}>
|
? html`<ha-md-menu-item @click=${this._showOptions}>
|
||||||
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.configure"
|
"ui.panel.config.integrations.config_entry.configure"
|
||||||
)}
|
)}
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
: ""}
|
: ""}
|
||||||
${item.disabled_by && devices.length
|
${item.disabled_by && devices.length
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
href=${devices.length === 1
|
href=${devices.length === 1
|
||||||
? `/config/devices/device/${devices[0].id}`
|
? `/config/devices/device/${devices[0].id}`
|
||||||
: `/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}`}
|
: `/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}`}
|
||||||
@ -824,11 +824,11 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
{ count: devices.length }
|
{ count: devices.length }
|
||||||
)}
|
)}
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${item.disabled_by && services.length
|
${item.disabled_by && services.length
|
||||||
? html`<ha-menu-item
|
? html`<ha-md-menu-item
|
||||||
href=${services.length === 1
|
href=${services.length === 1
|
||||||
? `/config/devices/device/${services[0].id}`
|
? `/config/devices/device/${services[0].id}`
|
||||||
: `/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}`}
|
: `/config/devices/dashboard?historyBack=1&config_entry=${item.entry_id}`}
|
||||||
@ -842,11 +842,11 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
{ count: services.length }
|
{ count: services.length }
|
||||||
)}
|
)}
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-menu-item> `
|
</ha-md-menu-item> `
|
||||||
: ""}
|
: ""}
|
||||||
${item.disabled_by && entities.length
|
${item.disabled_by && entities.length
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
href=${`/config/entities?historyBack=1&config_entry=${item.entry_id}`}
|
href=${`/config/entities?historyBack=1&config_entry=${item.entry_id}`}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
@ -858,7 +858,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
{ count: entities.length }
|
{ count: entities.length }
|
||||||
)}
|
)}
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${!item.disabled_by &&
|
${!item.disabled_by &&
|
||||||
@ -866,27 +866,27 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
item.supports_unload &&
|
item.supports_unload &&
|
||||||
item.source !== "system"
|
item.source !== "system"
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item @click=${this._handleReload}>
|
<ha-md-menu-item @click=${this._handleReload}>
|
||||||
<ha-svg-icon slot="start" .path=${mdiReload}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiReload}></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.reload"
|
"ui.panel.config.integrations.config_entry.reload"
|
||||||
)}
|
)}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-menu-item @click=${this._handleRename} graphic="icon">
|
<ha-md-menu-item @click=${this._handleRename} graphic="icon">
|
||||||
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiRenameBox}></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.rename"
|
"ui.panel.config.integrations.config_entry.rename"
|
||||||
)}
|
)}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
|
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
|
|
||||||
${this._diagnosticHandler && item.state === "loaded"
|
${this._diagnosticHandler && item.state === "loaded"
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
href=${getConfigEntryDiagnosticsDownloadUrl(item.entry_id)}
|
href=${getConfigEntryDiagnosticsDownloadUrl(item.entry_id)}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
@click=${this._signUrl}
|
@click=${this._signUrl}
|
||||||
@ -895,41 +895,41 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.download_diagnostics"
|
"ui.panel.config.integrations.config_entry.download_diagnostics"
|
||||||
)}
|
)}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${!item.disabled_by &&
|
${!item.disabled_by &&
|
||||||
item.supports_reconfigure &&
|
item.supports_reconfigure &&
|
||||||
item.source !== "system"
|
item.source !== "system"
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item @click=${this._handleReconfigure}>
|
<ha-md-menu-item @click=${this._handleReconfigure}>
|
||||||
<ha-svg-icon slot="start" .path=${mdiWrench}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiWrench}></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.reconfigure"
|
"ui.panel.config.integrations.config_entry.reconfigure"
|
||||||
)}
|
)}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
|
||||||
<ha-menu-item @click=${this._handleSystemOptions} graphic="icon">
|
<ha-md-menu-item @click=${this._handleSystemOptions} graphic="icon">
|
||||||
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
<ha-svg-icon slot="start" .path=${mdiCog}></ha-svg-icon>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.system_options"
|
"ui.panel.config.integrations.config_entry.system_options"
|
||||||
)}
|
)}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
${item.disabled_by === "user"
|
${item.disabled_by === "user"
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item @click=${this._handleEnable}>
|
<ha-md-menu-item @click=${this._handleEnable}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="start"
|
slot="start"
|
||||||
.path=${mdiPlayCircleOutline}
|
.path=${mdiPlayCircleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass.localize("ui.common.enable")}
|
${this.hass.localize("ui.common.enable")}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: item.source !== "system"
|
: item.source !== "system"
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item
|
<ha-md-menu-item
|
||||||
class="warning"
|
class="warning"
|
||||||
@click=${this._handleDisable}
|
@click=${this._handleDisable}
|
||||||
graphic="icon"
|
graphic="icon"
|
||||||
@ -940,12 +940,12 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
.path=${mdiStopCircleOutline}
|
.path=${mdiStopCircleOutline}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
${this.hass.localize("ui.common.disable")}
|
${this.hass.localize("ui.common.disable")}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
${item.source !== "system"
|
${item.source !== "system"
|
||||||
? html`
|
? html`
|
||||||
<ha-menu-item class="warning" @click=${this._handleDelete}>
|
<ha-md-menu-item class="warning" @click=${this._handleDelete}>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
slot="start"
|
slot="start"
|
||||||
class="warning"
|
class="warning"
|
||||||
@ -954,11 +954,11 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.integrations.config_entry.delete"
|
"ui.panel.config.integrations.config_entry.delete"
|
||||||
)}
|
)}
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
</ha-list-item-new>`;
|
</ha-md-list-item>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _highlightEntry() {
|
private async _highlightEntry() {
|
||||||
@ -1485,13 +1485,13 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
ha-alert:first-of-type {
|
ha-alert:first-of-type {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
ha-list-item-new {
|
ha-md-list-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
ha-list-item-new.discovered {
|
ha-md-list-item.discovered {
|
||||||
height: 72px;
|
height: 72px;
|
||||||
}
|
}
|
||||||
ha-list-item-new.config_entry::after {
|
ha-md-list-item.config_entry::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
@ -1561,7 +1561,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
|
|||||||
.state-disabled [slot="supporting-text"] {
|
.state-disabled [slot="supporting-text"] {
|
||||||
opacity: var(--md-list-item-disabled-opacity, 0.3);
|
opacity: var(--md-list-item-disabled-opacity, 0.3);
|
||||||
}
|
}
|
||||||
ha-list-new {
|
ha-md-list {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ import { LitElement, html } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||||
import "../../../../../../components/ha-icon-next";
|
import "../../../../../../components/ha-icon-next";
|
||||||
import "../../../../../../components/ha-list-item-new";
|
import "../../../../../../components/ha-md-list-item";
|
||||||
import "../../../../../../components/ha-list-new";
|
import "../../../../../../components/ha-md-list";
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
import { HomeAssistant } from "../../../../../../types";
|
||||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ import { LitElement, css, html } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||||
import "../../../../../../components/ha-icon-next";
|
import "../../../../../../components/ha-icon-next";
|
||||||
import "../../../../../../components/ha-list-item-new";
|
import "../../../../../../components/ha-md-list-item";
|
||||||
import "../../../../../../components/ha-list-new";
|
import "../../../../../../components/ha-md-list";
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
import { HomeAssistant } from "../../../../../../types";
|
||||||
import { MatterAddDeviceStep } from "../dialog-matter-add-device";
|
import { MatterAddDeviceStep } from "../dialog-matter-add-device";
|
||||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||||
@ -23,8 +23,8 @@ class MatterAddDeviceExisting extends LitElement {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ha-list-new>
|
<ha-md-list>
|
||||||
<ha-list-item-new
|
<ha-md-list-item
|
||||||
interactive
|
interactive
|
||||||
type="button"
|
type="button"
|
||||||
.step=${"google_home"}
|
.step=${"google_home"}
|
||||||
@ -43,8 +43,8 @@ class MatterAddDeviceExisting extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
<ha-list-item-new
|
<ha-md-list-item
|
||||||
interactive
|
interactive
|
||||||
type="button"
|
type="button"
|
||||||
.step=${"apple_home"}
|
.step=${"apple_home"}
|
||||||
@ -63,8 +63,8 @@ class MatterAddDeviceExisting extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
<ha-list-item-new
|
<ha-md-list-item
|
||||||
interactive
|
interactive
|
||||||
type="button"
|
type="button"
|
||||||
.step=${"generic"}
|
.step=${"generic"}
|
||||||
@ -80,8 +80,8 @@ class MatterAddDeviceExisting extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
</ha-list-new>
|
</ha-md-list>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import { LitElement, html } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||||
import "../../../../../../components/ha-icon-next";
|
import "../../../../../../components/ha-icon-next";
|
||||||
import "../../../../../../components/ha-list-item-new";
|
import "../../../../../../components/ha-md-list-item";
|
||||||
import "../../../../../../components/ha-list-new";
|
import "../../../../../../components/ha-md-list";
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
import { HomeAssistant } from "../../../../../../types";
|
||||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import { LitElement, html } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||||
import "../../../../../../components/ha-icon-next";
|
import "../../../../../../components/ha-icon-next";
|
||||||
import "../../../../../../components/ha-list-item-new";
|
import "../../../../../../components/ha-md-list-item";
|
||||||
import "../../../../../../components/ha-list-new";
|
import "../../../../../../components/ha-md-list";
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
import { HomeAssistant } from "../../../../../../types";
|
||||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import { LitElement, html } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||||
import "../../../../../../components/ha-icon-next";
|
import "../../../../../../components/ha-icon-next";
|
||||||
import "../../../../../../components/ha-list-item-new";
|
import "../../../../../../components/ha-md-list-item";
|
||||||
import "../../../../../../components/ha-list-new";
|
import "../../../../../../components/ha-md-list";
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
import { HomeAssistant } from "../../../../../../types";
|
||||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import { LitElement, html } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||||
import "../../../../../../components/ha-icon-next";
|
import "../../../../../../components/ha-icon-next";
|
||||||
import "../../../../../../components/ha-list-item-new";
|
import "../../../../../../components/ha-md-list-item";
|
||||||
import "../../../../../../components/ha-list-new";
|
import "../../../../../../components/ha-md-list";
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
import { HomeAssistant } from "../../../../../../types";
|
||||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ class MatterAddDeviceMain extends LitElement {
|
|||||||
${this.hass.localize(`ui.dialogs.matter-add-device.main.question`)}
|
${this.hass.localize(`ui.dialogs.matter-add-device.main.question`)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ha-list-new>
|
<ha-md-list>
|
||||||
<ha-list-item-new
|
<ha-md-list-item
|
||||||
interactive
|
interactive
|
||||||
type="button"
|
type="button"
|
||||||
.step=${"new"}
|
.step=${"new"}
|
||||||
@ -37,8 +37,8 @@ class MatterAddDeviceMain extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
<ha-list-item-new
|
<ha-md-list-item
|
||||||
interactive
|
interactive
|
||||||
type="button"
|
type="button"
|
||||||
.step=${"existing"}
|
.step=${"existing"}
|
||||||
@ -56,8 +56,8 @@ class MatterAddDeviceMain extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<ha-icon-next slot="end"></ha-icon-next>
|
<ha-icon-next slot="end"></ha-icon-next>
|
||||||
</ha-list-item-new>
|
</ha-md-list-item>
|
||||||
</ha-list-new>
|
</ha-md-list>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ export const sharedStyles = css`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
ha-list-new {
|
ha-md-list {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
--md-list-item-leading-space: var(--horizontal-padding, 16px);
|
--md-list-item-leading-space: var(--horizontal-padding, 16px);
|
||||||
--md-list-item-trailing-space: var(--horizontal-padding, 16px);
|
--md-list-item-trailing-space: var(--horizontal-padding, 16px);
|
||||||
|
@ -106,13 +106,11 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.thread.add_dataset_from_tlv"
|
"ui.panel.config.thread.add_dataset_from_tlv"
|
||||||
)}</mwc-list-item
|
)}</mwc-list-item
|
||||||
>
|
>
|
||||||
${!this._otbrInfo
|
<mwc-list-item @click=${this._addOTBR}
|
||||||
? html`<mwc-list-item @click=${this._addOTBR}
|
|
||||||
>${this.hass.localize(
|
>${this.hass.localize(
|
||||||
"ui.panel.config.thread.add_open_thread_border_router"
|
"ui.panel.config.thread.add_open_thread_border_router"
|
||||||
)}</mwc-list-item
|
)}</mwc-list-item
|
||||||
>`
|
>
|
||||||
: ""}
|
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>${this.hass.localize("ui.panel.config.thread.my_network")}</h1>
|
<h1>${this.hass.localize("ui.panel.config.thread.my_network")}</h1>
|
||||||
@ -342,8 +340,8 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
|||||||
type: "thread/store_in_platform_keychain",
|
type: "thread/store_in_platform_keychain",
|
||||||
payload: {
|
payload: {
|
||||||
mac_extended_address: otbr.extended_address,
|
mac_extended_address: otbr.extended_address,
|
||||||
border_agent_id: otbr.border_agent_id ?? "",
|
border_agent_id: otbr.border_agent_id,
|
||||||
active_operational_dataset: otbr.active_dataset_tlvs ?? "",
|
active_operational_dataset: otbr.active_dataset_tlvs,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -153,8 +153,9 @@ class DialogZWaveJSAddNode extends LitElement {
|
|||||||
.label=${html`<b>Secure if possible</b>
|
.label=${html`<b>Secure if possible</b>
|
||||||
<div class="secondary">
|
<div class="secondary">
|
||||||
Requires user interaction during inclusion. Fast and
|
Requires user interaction during inclusion. Fast and
|
||||||
secure with S2 when supported. Fallback to legacy S0
|
secure with S2 when supported. Allows manually
|
||||||
or no encryption when necessary.
|
selecting which security keys to grant. Fallback to
|
||||||
|
legacy S0 or no encryption when necessary.
|
||||||
</div>`}
|
</div>`}
|
||||||
>
|
>
|
||||||
<ha-radio
|
<ha-radio
|
||||||
|
@ -6,46 +6,39 @@ import {
|
|||||||
mdiCloseCircle,
|
mdiCloseCircle,
|
||||||
mdiProgressClock,
|
mdiProgressClock,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
|
||||||
import {
|
import {
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
html,
|
|
||||||
LitElement,
|
LitElement,
|
||||||
nothing,
|
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import memoizeOne from "memoize-one";
|
import { groupBy } from "../../../../../common/util/group-by";
|
||||||
import "../../../../../components/ha-alert";
|
import "../../../../../components/ha-alert";
|
||||||
import "../../../../../components/ha-card";
|
import "../../../../../components/ha-card";
|
||||||
import "../../../../../components/ha-icon-next";
|
import "../../../../../components/ha-icon-next";
|
||||||
import "../../../../../components/ha-select";
|
import "../../../../../components/ha-select";
|
||||||
import "../../../../../components/ha-settings-row";
|
import "../../../../../components/ha-settings-row";
|
||||||
import "../../../../../components/ha-svg-icon";
|
import "../../../../../components/ha-svg-icon";
|
||||||
import "../../../../../components/ha-switch";
|
|
||||||
import "../../../../../components/ha-textfield";
|
import "../../../../../components/ha-textfield";
|
||||||
import { groupBy } from "../../../../../common/util/group-by";
|
import "../../../../../components/ha-selector/ha-selector-boolean";
|
||||||
import {
|
import { computeDeviceName } from "../../../../../data/device_registry";
|
||||||
computeDeviceName,
|
|
||||||
DeviceRegistryEntry,
|
|
||||||
subscribeDeviceRegistry,
|
|
||||||
} from "../../../../../data/device_registry";
|
|
||||||
import {
|
import {
|
||||||
|
ZWaveJSNodeConfigParam,
|
||||||
|
ZWaveJSNodeConfigParams,
|
||||||
|
ZWaveJSSetConfigParamResult,
|
||||||
|
ZwaveJSNodeMetadata,
|
||||||
fetchZwaveNodeConfigParameters,
|
fetchZwaveNodeConfigParameters,
|
||||||
fetchZwaveNodeMetadata,
|
fetchZwaveNodeMetadata,
|
||||||
setZwaveNodeConfigParameter,
|
setZwaveNodeConfigParameter,
|
||||||
ZWaveJSNodeConfigParam,
|
|
||||||
ZWaveJSNodeConfigParams,
|
|
||||||
ZwaveJSNodeMetadata,
|
|
||||||
ZWaveJSSetConfigParamResult,
|
|
||||||
} from "../../../../../data/zwave_js";
|
} from "../../../../../data/zwave_js";
|
||||||
import "../../../../../layouts/hass-error-screen";
|
import "../../../../../layouts/hass-error-screen";
|
||||||
import "../../../../../layouts/hass-loading-screen";
|
import "../../../../../layouts/hass-loading-screen";
|
||||||
import "../../../../../layouts/hass-tabs-subpage";
|
import "../../../../../layouts/hass-tabs-subpage";
|
||||||
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
|
|
||||||
import { haStyle } from "../../../../../resources/styles";
|
import { haStyle } from "../../../../../resources/styles";
|
||||||
import type { HomeAssistant, Route } from "../../../../../types";
|
import type { HomeAssistant, Route } from "../../../../../types";
|
||||||
import "../../../ha-config-section";
|
import "../../../ha-config-section";
|
||||||
@ -57,16 +50,8 @@ const icons = {
|
|||||||
error: mdiCloseCircle,
|
error: mdiCloseCircle,
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDevice = memoizeOne(
|
|
||||||
(
|
|
||||||
deviceId: string,
|
|
||||||
entries?: DeviceRegistryEntry[]
|
|
||||||
): DeviceRegistryEntry | undefined =>
|
|
||||||
entries?.find((device) => device.id === deviceId)
|
|
||||||
);
|
|
||||||
|
|
||||||
@customElement("zwave_js-node-config")
|
@customElement("zwave_js-node-config")
|
||||||
class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
class ZWaveJSNodeConfig extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
@ -79,8 +64,6 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property() public deviceId!: string;
|
@property() public deviceId!: string;
|
||||||
|
|
||||||
@state() private _deviceRegistryEntries?: DeviceRegistryEntry[];
|
|
||||||
|
|
||||||
@state() private _nodeMetadata?: ZwaveJSNodeMetadata;
|
@state() private _nodeMetadata?: ZwaveJSNodeMetadata;
|
||||||
|
|
||||||
@state() private _config?: ZWaveJSNodeConfigParams;
|
@state() private _config?: ZWaveJSNodeConfigParams;
|
||||||
@ -94,19 +77,8 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
this.deviceId = this.route.path.substr(1);
|
this.deviceId = this.route.path.substr(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public hassSubscribe(): UnsubscribeFunc[] {
|
|
||||||
return [
|
|
||||||
subscribeDeviceRegistry(this.hass.connection, (entries) => {
|
|
||||||
this._deviceRegistryEntries = entries;
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues): void {
|
protected updated(changedProps: PropertyValues): void {
|
||||||
if (
|
if (!this._config || changedProps.has("deviceId")) {
|
||||||
(!this._config || changedProps.has("deviceId")) &&
|
|
||||||
changedProps.has("_deviceRegistryEntries")
|
|
||||||
) {
|
|
||||||
this._fetchData();
|
this._fetchData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,7 +97,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
return html`<hass-loading-screen></hass-loading-screen>`;
|
return html`<hass-loading-screen></hass-loading-screen>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const device = this._device!;
|
const device = this.hass.devices[this.deviceId];
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
@ -217,6 +189,11 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
item: ZWaveJSNodeConfigParam
|
item: ZWaveJSNodeConfigParam
|
||||||
): TemplateResult {
|
): TemplateResult {
|
||||||
const result = this._results[id];
|
const result = this._results[id];
|
||||||
|
|
||||||
|
const isTypeBoolean =
|
||||||
|
item.configuration_value_type === "boolean" ||
|
||||||
|
this._isEnumeratedBool(item);
|
||||||
|
|
||||||
const labelAndDescription = html`
|
const labelAndDescription = html`
|
||||||
<span slot="prefix" class="prefix">
|
<span slot="prefix" class="prefix">
|
||||||
${this.hass.localize("ui.panel.config.zwave_js.node_config.parameter")}
|
${this.hass.localize("ui.panel.config.zwave_js.node_config.parameter")}
|
||||||
@ -268,23 +245,36 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const defaultLabel =
|
||||||
|
item.metadata.writeable && item.metadata.default !== undefined
|
||||||
|
? `${this.hass.localize("ui.panel.config.zwave_js.node_config.default")}:
|
||||||
|
${
|
||||||
|
isTypeBoolean
|
||||||
|
? this.hass.localize(
|
||||||
|
item.metadata.default === 1 ? "ui.common.yes" : "ui.common.no"
|
||||||
|
)
|
||||||
|
: item.configuration_value_type === "enumerated"
|
||||||
|
? item.metadata.states[item.metadata.default] ||
|
||||||
|
item.metadata.default
|
||||||
|
: item.metadata.default
|
||||||
|
}`
|
||||||
|
: "";
|
||||||
|
|
||||||
// Numeric entries with a min value of 0 and max of 1 are considered boolean
|
// Numeric entries with a min value of 0 and max of 1 are considered boolean
|
||||||
if (
|
if (isTypeBoolean) {
|
||||||
item.configuration_value_type === "boolean" ||
|
|
||||||
this._isEnumeratedBool(item)
|
|
||||||
) {
|
|
||||||
return html`
|
return html`
|
||||||
${labelAndDescription}
|
${labelAndDescription}
|
||||||
<div class="switch">
|
<div class="switch">
|
||||||
<ha-switch
|
<ha-selector-boolean
|
||||||
.property=${item.property}
|
.property=${item.property}
|
||||||
.endpoint=${item.endpoint}
|
.endpoint=${item.endpoint}
|
||||||
.propertyKey=${item.property_key}
|
.propertyKey=${item.property_key}
|
||||||
.checked=${item.value === 1}
|
.value=${item.value === 1}
|
||||||
.key=${id}
|
.key=${id}
|
||||||
@change=${this._switchToggled}
|
@change=${this._switchToggled}
|
||||||
.disabled=${!item.metadata.writeable}
|
.disabled=${!item.metadata.writeable}
|
||||||
></ha-switch>
|
.helper=${defaultLabel}
|
||||||
|
></ha-selector-boolean>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -303,6 +293,8 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
.disabled=${!item.metadata.writeable}
|
.disabled=${!item.metadata.writeable}
|
||||||
@change=${this._numericInputChanged}
|
@change=${this._numericInputChanged}
|
||||||
.suffix=${item.metadata.unit}
|
.suffix=${item.metadata.unit}
|
||||||
|
.helper=${`${this.hass.localize("ui.panel.config.zwave_js.node_config.between_min_max", { min: item.metadata.min, max: item.metadata.max })}${defaultLabel ? `, ${defaultLabel}` : ""}`}
|
||||||
|
helperPersistent
|
||||||
>
|
>
|
||||||
</ha-textfield>`;
|
</ha-textfield>`;
|
||||||
}
|
}
|
||||||
@ -318,6 +310,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
.endpoint=${item.endpoint}
|
.endpoint=${item.endpoint}
|
||||||
.propertyKey=${item.property_key}
|
.propertyKey=${item.property_key}
|
||||||
@selected=${this._dropdownSelected}
|
@selected=${this._dropdownSelected}
|
||||||
|
.helper=${defaultLabel}
|
||||||
>
|
>
|
||||||
${Object.entries(item.metadata.states).map(
|
${Object.entries(item.metadata.states).map(
|
||||||
([key, entityState]) => html`
|
([key, entityState]) => html`
|
||||||
@ -384,6 +377,19 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
if (Number(this._config![ev.target.key].value) === value) {
|
if (Number(this._config![ev.target.key].value) === value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
(ev.target.min !== undefined && value < ev.target.min) ||
|
||||||
|
(ev.target.max !== undefined && value > ev.target.max)
|
||||||
|
) {
|
||||||
|
this.setError(
|
||||||
|
ev.target.key,
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.panel.config.zwave_js.node_config.error_not_in_range",
|
||||||
|
{ min: ev.target.min, max: ev.target.max }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setResult(ev.target.key, undefined);
|
this.setResult(ev.target.key, undefined);
|
||||||
this._updateConfigParameter(ev.target, value);
|
this._updateConfigParameter(ev.target, value);
|
||||||
}
|
}
|
||||||
@ -392,7 +398,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
try {
|
try {
|
||||||
const result = await setZwaveNodeConfigParameter(
|
const result = await setZwaveNodeConfigParameter(
|
||||||
this.hass,
|
this.hass,
|
||||||
this._device!.id,
|
this.deviceId,
|
||||||
target.property,
|
target.property,
|
||||||
target.endpoint,
|
target.endpoint,
|
||||||
value,
|
value,
|
||||||
@ -420,16 +426,12 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
|
|||||||
this._results = { ...this._results, [key]: errorParam };
|
this._results = { ...this._results, [key]: errorParam };
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _device(): DeviceRegistryEntry | undefined {
|
|
||||||
return getDevice(this.deviceId, this._deviceRegistryEntries);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _fetchData() {
|
private async _fetchData() {
|
||||||
if (!this.configEntryId || !this._deviceRegistryEntries) {
|
if (!this.configEntryId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const device = this._device;
|
const device = this.hass.devices[this.deviceId];
|
||||||
if (!device) {
|
if (!device) {
|
||||||
this._error = "device_not_found";
|
this._error = "device_not_found";
|
||||||
return;
|
return;
|
||||||
|
@ -250,7 +250,7 @@ export class HaConfigLogs extends LitElement {
|
|||||||
--mdc-theme-primary: var(--primary-text-color);
|
--mdc-theme-primary: var(--primary-text-color);
|
||||||
--mdc-icon-size: 36px;
|
--mdc-icon-size: 36px;
|
||||||
}
|
}
|
||||||
ha-button-menu > mwc-button > ha-svg-icon {
|
ha-button-menu > ha-button > ha-svg-icon {
|
||||||
margin-inline-end: 0px;
|
margin-inline-end: 0px;
|
||||||
margin-inline-start: 8px;
|
margin-inline-start: 8px;
|
||||||
}
|
}
|
||||||
|
@ -322,8 +322,6 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
hasFab
|
hasFab
|
||||||
clickable
|
clickable
|
||||||
>
|
>
|
||||||
${this.hass.userData?.showAdvanced
|
|
||||||
? html`
|
|
||||||
<ha-button-menu slot="toolbar-icon" activatable>
|
<ha-button-menu slot="toolbar-icon" activatable>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
@ -331,13 +329,9 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-clickable-list-item href="/config/lovelace/resources">
|
<ha-clickable-list-item href="/config/lovelace/resources">
|
||||||
${this.hass.localize(
|
${this.hass.localize("ui.panel.config.lovelace.resources.caption")}
|
||||||
"ui.panel.config.lovelace.resources.caption"
|
|
||||||
)}
|
|
||||||
</ha-clickable-list-item>
|
</ha-clickable-list-item>
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<ha-fab
|
<ha-fab
|
||||||
slot="fab"
|
slot="fab"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import { CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state, query } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
import { mdiClose } from "@mdi/js";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
import "../../../../components/ha-md-dialog";
|
||||||
|
import type { HaMdDialog } from "../../../../components/ha-md-dialog";
|
||||||
|
import "../../../../components/ha-dialog-header";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
|
import "../../../../components/ha-icon-button";
|
||||||
import { SchemaUnion } from "../../../../components/ha-form/types";
|
import { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
import { LovelaceResourcesMutableParams } from "../../../../data/lovelace/resource";
|
import { LovelaceResourcesMutableParams } from "../../../../data/lovelace/resource";
|
||||||
import { haStyleDialog } from "../../../../resources/styles";
|
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceResourceDetailsDialogParams } from "./show-dialog-lovelace-resource-detail";
|
import { LovelaceResourceDetailsDialogParams } from "./show-dialog-lovelace-resource-detail";
|
||||||
|
|
||||||
@ -40,6 +43,8 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
|
|
||||||
@state() private _submitting = false;
|
@state() private _submitting = false;
|
||||||
|
|
||||||
|
@query("ha-md-dialog") private _dialog?: HaMdDialog;
|
||||||
|
|
||||||
public showDialog(params: LovelaceResourceDetailsDialogParams): void {
|
public showDialog(params: LovelaceResourceDetailsDialogParams): void {
|
||||||
this._params = params;
|
this._params = params;
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
@ -55,32 +60,52 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog(): void {
|
private _dialogClosed(): void {
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public closeDialog(): void {
|
||||||
|
this._dialog?.close();
|
||||||
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
if (!this._params) {
|
if (!this._params) {
|
||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
const urlInvalid = !this._data?.url || this._data.url.trim() === "";
|
const urlInvalid = !this._data?.url || this._data.url.trim() === "";
|
||||||
return html`
|
|
||||||
<ha-dialog
|
const dialogTitle =
|
||||||
open
|
this._params.resource?.url ||
|
||||||
@closed=${this.closeDialog}
|
this.hass!.localize(
|
||||||
scrimClickAction
|
"ui.panel.config.lovelace.resources.detail.new_resource"
|
||||||
escapeKeyAction
|
);
|
||||||
.heading=${createCloseHeading(
|
|
||||||
this.hass,
|
const ariaLabel = this._params.resource?.url
|
||||||
this._params.resource
|
? this.hass!.localize(
|
||||||
? this._params.resource.url
|
"ui.panel.config.lovelace.resources.detail.edit_resource"
|
||||||
|
)
|
||||||
: this.hass!.localize(
|
: this.hass!.localize(
|
||||||
"ui.panel.config.lovelace.resources.detail.new_resource"
|
"ui.panel.config.lovelace.resources.detail.new_resource"
|
||||||
)
|
);
|
||||||
)}
|
|
||||||
|
return html`
|
||||||
|
<ha-md-dialog
|
||||||
|
open
|
||||||
|
disable-cancel-action
|
||||||
|
@closed=${this._dialogClosed}
|
||||||
|
.ariaLabel=${ariaLabel}
|
||||||
>
|
>
|
||||||
<div>
|
<ha-dialog-header slot="headline">
|
||||||
|
<ha-icon-button
|
||||||
|
slot="navigationIcon"
|
||||||
|
.label=${this.hass.localize("ui.dialogs.generic.close") ?? "Close"}
|
||||||
|
.path=${mdiClose}
|
||||||
|
@click=${this.closeDialog}
|
||||||
|
></ha-icon-button>
|
||||||
|
<span slot="title" .title=${dialogTitle}> ${dialogTitle} </span>
|
||||||
|
</ha-dialog-header>
|
||||||
|
<div slot="content">
|
||||||
<ha-alert
|
<ha-alert
|
||||||
alert-type="warning"
|
alert-type="warning"
|
||||||
.title=${this.hass!.localize(
|
.title=${this.hass!.localize(
|
||||||
@ -101,22 +126,11 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
</div>
|
</div>
|
||||||
${this._params.resource
|
<div slot="actions">
|
||||||
? html`
|
<mwc-button @click=${this.closeDialog}>
|
||||||
<mwc-button
|
${this.hass!.localize("ui.common.cancel")}
|
||||||
slot="secondaryAction"
|
|
||||||
class="warning"
|
|
||||||
@click=${this._deleteResource}
|
|
||||||
.disabled=${this._submitting}
|
|
||||||
>
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.panel.config.lovelace.resources.detail.delete"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
<mwc-button
|
<mwc-button
|
||||||
slot="primaryAction"
|
|
||||||
@click=${this._updateResource}
|
@click=${this._updateResource}
|
||||||
.disabled=${urlInvalid || !this._data?.res_type || this._submitting}
|
.disabled=${urlInvalid || !this._data?.res_type || this._submitting}
|
||||||
>
|
>
|
||||||
@ -128,7 +142,8 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
"ui.panel.config.lovelace.resources.detail.create"
|
"ui.panel.config.lovelace.resources.detail.create"
|
||||||
)}
|
)}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</ha-dialog>
|
</div>
|
||||||
|
</ha-md-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,21 +246,6 @@ export class DialogLovelaceResourceDetail extends LitElement {
|
|||||||
this._submitting = false;
|
this._submitting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _deleteResource() {
|
|
||||||
this._submitting = true;
|
|
||||||
try {
|
|
||||||
if (await this._params!.removeResource()) {
|
|
||||||
this.closeDialog();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
this._submitting = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return haStyleDialog;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { mdiPlus } from "@mdi/js";
|
import { mdiDelete, mdiPlus } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@ -109,6 +109,20 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
) || resource.type}
|
) || resource.type}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
delete: {
|
||||||
|
title: "",
|
||||||
|
type: "icon-button",
|
||||||
|
minWidth: "48px",
|
||||||
|
maxWidth: "48px",
|
||||||
|
showNarrow: true,
|
||||||
|
template: (resource) =>
|
||||||
|
html`<ha-icon-button
|
||||||
|
@click=${this._removeResource}
|
||||||
|
.label=${this.hass.localize("ui.common.delete")}
|
||||||
|
.path=${mdiDelete}
|
||||||
|
.resource=${resource}
|
||||||
|
></ha-icon-button>`,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -235,7 +249,12 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
);
|
);
|
||||||
loadLovelaceResources([updated], this.hass!);
|
loadLovelaceResources([updated], this.hass!);
|
||||||
},
|
},
|
||||||
removeResource: async () => {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeResource = async (event: any) => {
|
||||||
|
const resource = event.currentTarget.resource as LovelaceResource;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!(await showConfirmationDialog(this, {
|
!(await showConfirmationDialog(this, {
|
||||||
title: this.hass!.localize(
|
title: this.hass!.localize(
|
||||||
@ -243,7 +262,7 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
),
|
),
|
||||||
text: this.hass!.localize(
|
text: this.hass!.localize(
|
||||||
"ui.panel.config.lovelace.resources.confirm_delete_text",
|
"ui.panel.config.lovelace.resources.confirm_delete_text",
|
||||||
{ url: resource!.url }
|
{ url: resource.url }
|
||||||
),
|
),
|
||||||
dismissText: this.hass!.localize("ui.common.cancel"),
|
dismissText: this.hass!.localize("ui.common.cancel"),
|
||||||
confirmText: this.hass!.localize("ui.common.delete"),
|
confirmText: this.hass!.localize("ui.common.delete"),
|
||||||
@ -254,8 +273,8 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await deleteResource(this.hass!, resource!.id);
|
await deleteResource(this.hass!, resource.id);
|
||||||
this._resources = this._resources!.filter((res) => res !== resource);
|
this._resources = this._resources!.filter(({ id }) => id !== resource.id);
|
||||||
showConfirmationDialog(this, {
|
showConfirmationDialog(this, {
|
||||||
title: this.hass!.localize(
|
title: this.hass!.localize(
|
||||||
"ui.panel.config.lovelace.resources.refresh_header"
|
"ui.panel.config.lovelace.resources.refresh_header"
|
||||||
@ -271,9 +290,7 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleSortingChanged(ev: CustomEvent) {
|
private _handleSortingChanged(ev: CustomEvent) {
|
||||||
this._activeSorting = ev.detail;
|
this._activeSorting = ev.detail;
|
||||||
|
@ -10,7 +10,6 @@ export interface LovelaceResourceDetailsDialogParams {
|
|||||||
updateResource: (
|
updateResource: (
|
||||||
updates: Partial<LovelaceResourcesMutableParams>
|
updates: Partial<LovelaceResourcesMutableParams>
|
||||||
) => Promise<unknown>;
|
) => Promise<unknown>;
|
||||||
removeResource: () => Promise<boolean>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadResourceDetailDialog = () =>
|
export const loadResourceDetailDialog = () =>
|
||||||
|
@ -1,16 +1,34 @@
|
|||||||
import { html } from "lit";
|
import { html } from "lit";
|
||||||
|
import { DataEntryFlowStep } from "../../../data/data_entry_flow";
|
||||||
import { domainToName } from "../../../data/integration";
|
import { domainToName } from "../../../data/integration";
|
||||||
import {
|
import {
|
||||||
|
RepairsIssue,
|
||||||
createRepairsFlow,
|
createRepairsFlow,
|
||||||
deleteRepairsFlow,
|
deleteRepairsFlow,
|
||||||
fetchRepairsFlow,
|
fetchRepairsFlow,
|
||||||
handleRepairsFlowStep,
|
handleRepairsFlowStep,
|
||||||
RepairsIssue,
|
|
||||||
} from "../../../data/repairs";
|
} from "../../../data/repairs";
|
||||||
import {
|
import {
|
||||||
loadDataEntryFlowDialog,
|
loadDataEntryFlowDialog,
|
||||||
showFlowDialog,
|
showFlowDialog,
|
||||||
} from "../../../dialogs/config-flow/show-dialog-data-entry-flow";
|
} from "../../../dialogs/config-flow/show-dialog-data-entry-flow";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
|
||||||
|
const mergePlaceholders = (issue: RepairsIssue, step: DataEntryFlowStep) =>
|
||||||
|
step.description_placeholders && issue.translation_placeholders
|
||||||
|
? { ...issue.translation_placeholders, ...step.description_placeholders }
|
||||||
|
: step.description_placeholders || issue.translation_placeholders;
|
||||||
|
|
||||||
|
const renderIssueDescription = (hass: HomeAssistant, issue: RepairsIssue) =>
|
||||||
|
issue.breaks_in_ha_version
|
||||||
|
? html`
|
||||||
|
<ha-alert alert-type="warning">
|
||||||
|
${hass.localize("ui.panel.config.repairs.dialog.breaks_in_version", {
|
||||||
|
version: issue.breaks_in_ha_version,
|
||||||
|
})} </ha-alert
|
||||||
|
><br />
|
||||||
|
`
|
||||||
|
: "";
|
||||||
|
|
||||||
export const loadRepairFlowDialog = loadDataEntryFlowDialog;
|
export const loadRepairFlowDialog = loadDataEntryFlowDialog;
|
||||||
|
|
||||||
@ -53,10 +71,11 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.abort.${step.reason}`,
|
}.fix_flow.abort.${step.reason}`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
);
|
);
|
||||||
|
|
||||||
return description
|
return html`${renderIssueDescription(hass, issue)}
|
||||||
|
${description
|
||||||
? html`
|
? html`
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
breaks
|
breaks
|
||||||
@ -64,7 +83,7 @@ export const showRepairsFlowDialog = (
|
|||||||
.content=${description}
|
.content=${description}
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
`
|
`
|
||||||
: step.reason;
|
: step.reason}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderShowFormStepHeader(hass, step) {
|
renderShowFormStepHeader(hass, step) {
|
||||||
@ -73,7 +92,7 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.step.${step.step_id}.title`,
|
}.fix_flow.step.${step.step_id}.title`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
) || hass.localize("ui.dialogs.repair_flow.form.header")
|
) || hass.localize("ui.dialogs.repair_flow.form.header")
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -83,9 +102,10 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.step.${step.step_id}.description`,
|
}.fix_flow.step.${step.step_id}.description`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
);
|
);
|
||||||
return description
|
return html`${renderIssueDescription(hass, issue)}
|
||||||
|
${description
|
||||||
? html`
|
? html`
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
allowsvg
|
allowsvg
|
||||||
@ -93,7 +113,7 @@ export const showRepairsFlowDialog = (
|
|||||||
.content=${description}
|
.content=${description}
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
`
|
`
|
||||||
: "";
|
: ""}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderShowFormStepFieldLabel(hass, step, field, options) {
|
renderShowFormStepFieldLabel(hass, step, field, options) {
|
||||||
@ -101,7 +121,7 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.step.${step.step_id}.${options?.prefix ? `section.${options.prefix[0]}.` : ""}data.${field.name}`,
|
}.fix_flow.step.${step.step_id}.${options?.prefix ? `section.${options.prefix[0]}.` : ""}data.${field.name}`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -110,11 +130,12 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.step.${step.step_id}.${options?.prefix ? `section.${options.prefix[0]}.` : ""}data_description.${field.name}`,
|
}.fix_flow.step.${step.step_id}.${options?.prefix ? `section.${options.prefix[0]}.` : ""}data_description.${field.name}`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
);
|
);
|
||||||
return description
|
return html`${renderIssueDescription(hass, issue)}
|
||||||
|
${description
|
||||||
? html`<ha-markdown breaks .content=${description}></ha-markdown>`
|
? html`<ha-markdown breaks .content=${description}></ha-markdown>`
|
||||||
: "";
|
: ""}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderShowFormStepFieldError(hass, step, error) {
|
renderShowFormStepFieldError(hass, step, error) {
|
||||||
@ -122,7 +143,7 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.error.${error}`,
|
}.fix_flow.error.${error}`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -165,7 +186,7 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.step.${
|
`component.${issue.domain}.issues.step.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.${step.step_id}.title`,
|
}.fix_flow.${step.step_id}.title`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
) || hass.localize(`component.${issue.domain}.title`)
|
) || hass.localize(`component.${issue.domain}.title`)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -175,9 +196,9 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.progress.${step.progress_action}`,
|
}.fix_flow.progress.${step.progress_action}`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
);
|
);
|
||||||
return description
|
return html`${renderIssueDescription(hass, issue)}${description
|
||||||
? html`
|
? html`
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
allowsvg
|
allowsvg
|
||||||
@ -185,7 +206,7 @@ export const showRepairsFlowDialog = (
|
|||||||
.content=${description}
|
.content=${description}
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
`
|
`
|
||||||
: "";
|
: ""}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderMenuHeader(hass, step) {
|
renderMenuHeader(hass, step) {
|
||||||
@ -194,7 +215,7 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.step.${step.step_id}.title`,
|
}.fix_flow.step.${step.step_id}.title`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
) || hass.localize(`component.${issue.domain}.title`)
|
) || hass.localize(`component.${issue.domain}.title`)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -204,9 +225,10 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.step.${step.step_id}.description`,
|
}.fix_flow.step.${step.step_id}.description`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
);
|
);
|
||||||
return description
|
return html`${renderIssueDescription(hass, issue)}
|
||||||
|
${description
|
||||||
? html`
|
? html`
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
allowsvg
|
allowsvg
|
||||||
@ -214,7 +236,7 @@ export const showRepairsFlowDialog = (
|
|||||||
.content=${description}
|
.content=${description}
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
`
|
`
|
||||||
: "";
|
: ""}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderMenuOption(hass, step, option) {
|
renderMenuOption(hass, step, option) {
|
||||||
@ -222,7 +244,7 @@ export const showRepairsFlowDialog = (
|
|||||||
`component.${issue.domain}.issues.${
|
`component.${issue.domain}.issues.${
|
||||||
issue.translation_key || issue.issue_id
|
issue.translation_key || issue.issue_id
|
||||||
}.fix_flow.step.${step.step_id}.menu_options.${option}`,
|
}.fix_flow.step.${step.step_id}.menu_options.${option}`,
|
||||||
step.description_placeholders
|
mergePlaceholders(issue, step)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ import "../../../components/ha-filter-floor-areas";
|
|||||||
import "../../../components/ha-filter-labels";
|
import "../../../components/ha-filter-labels";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-icon-overflow-menu";
|
import "../../../components/ha-icon-overflow-menu";
|
||||||
import "../../../components/ha-menu-item";
|
import "../../../components/ha-md-menu-item";
|
||||||
import "../../../components/ha-state-icon";
|
import "../../../components/ha-state-icon";
|
||||||
import "../../../components/ha-sub-menu";
|
import "../../../components/ha-sub-menu";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
@ -423,7 +423,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const categoryItems = html`${this._categories?.map(
|
const categoryItems = html`${this._categories?.map(
|
||||||
(category) =>
|
(category) =>
|
||||||
html`<ha-menu-item
|
html`<ha-md-menu-item
|
||||||
.value=${category.category_id}
|
.value=${category.category_id}
|
||||||
@click=${this._handleBulkCategory}
|
@click=${this._handleBulkCategory}
|
||||||
>
|
>
|
||||||
@ -431,21 +431,21 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
|
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
|
||||||
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
|
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
|
||||||
<div slot="headline">${category.name}</div>
|
<div slot="headline">${category.name}</div>
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
)}
|
)}
|
||||||
<ha-menu-item .value=${null} @click=${this._handleBulkCategory}>
|
<ha-md-menu-item .value=${null} @click=${this._handleBulkCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.no_category"
|
"ui.panel.config.automation.picker.bulk_actions.no_category"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateCategory}>
|
<ha-md-menu-item @click=${this._bulkCreateCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
|
|
||||||
const labelItems = html` ${this._labels?.map((label) => {
|
const labelItems = html` ${this._labels?.map((label) => {
|
||||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||||
@ -457,7 +457,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
this._selected.some((entityId) =>
|
this._selected.some((entityId) =>
|
||||||
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
||||||
);
|
);
|
||||||
return html`<ha-menu-item
|
return html`<ha-md-menu-item
|
||||||
.value=${label.label_id}
|
.value=${label.label_id}
|
||||||
.action=${selected ? "remove" : "add"}
|
.action=${selected ? "remove" : "add"}
|
||||||
@click=${this._handleBulkLabel}
|
@click=${this._handleBulkLabel}
|
||||||
@ -475,18 +475,18 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
${label.name}
|
${label.name}
|
||||||
</ha-label>
|
</ha-label>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateLabel}>
|
<ha-md-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-md-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
|
||||||
const areaItems = html`${Object.values(this.hass.areas).map(
|
const areaItems = html`${Object.values(this.hass.areas).map(
|
||||||
(area) =>
|
(area) =>
|
||||||
html`<ha-menu-item
|
html`<ha-md-menu-item
|
||||||
.value=${area.area_id}
|
.value=${area.area_id}
|
||||||
@click=${this._handleBulkArea}
|
@click=${this._handleBulkArea}
|
||||||
>
|
>
|
||||||
@ -497,23 +497,23 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
.path=${mdiTextureBox}
|
.path=${mdiTextureBox}
|
||||||
></ha-svg-icon>`}
|
></ha-svg-icon>`}
|
||||||
<div slot="headline">${area.name}</div>
|
<div slot="headline">${area.name}</div>
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
)}
|
)}
|
||||||
<ha-menu-item .value=${null} @click=${this._handleBulkArea}>
|
<ha-md-menu-item .value=${null} @click=${this._handleBulkArea}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateArea}>
|
<ha-md-menu-item @click=${this._bulkCreateArea}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
|
|
||||||
const areasInOverflow =
|
const areasInOverflow =
|
||||||
(this._sizeController.value && this._sizeController.value < 900) ||
|
(this._sizeController.value && this._sizeController.value < 900) ||
|
||||||
@ -637,7 +637,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-filter-categories>
|
></ha-filter-categories>
|
||||||
|
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`<ha-button-menu-new slot="selection-bar">
|
? html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -650,10 +650,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${categoryItems}
|
${categoryItems}
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
${labelsInOverflow
|
${labelsInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -666,10 +666,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>`}
|
</ha-md-button-menu>`}
|
||||||
${areasInOverflow
|
${areasInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -682,11 +682,11 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${areaItems}
|
${areaItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-md-button-menu>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || areasInOverflow
|
${this.narrow || areasInOverflow
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-md-button-menu has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-assist-chip
|
? html`<ha-assist-chip
|
||||||
@ -714,7 +714,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.move_category"
|
"ui.panel.config.automation.picker.bulk_actions.move_category"
|
||||||
@ -724,7 +724,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${categoryItems}</ha-menu>
|
<ha-menu slot="menu">${categoryItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
@ -732,7 +732,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow || labelsInOverflow
|
this.narrow || labelsInOverflow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
||||||
@ -742,7 +742,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${labelItems}</ha-menu>
|
<ha-menu slot="menu">${labelItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
@ -750,7 +750,7 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow || areasInOverflow
|
this.narrow || areasInOverflow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
@ -760,12 +760,12 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${areaItems}</ha-menu>
|
<ha-menu slot="menu">${areaItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
</ha-button-menu-new>`
|
</ha-md-button-menu>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${!this.scenes.length
|
${!this.scenes.length
|
||||||
? html`<div class="empty" slot="empty">
|
? html`<div class="empty" slot="empty">
|
||||||
@ -1204,7 +1204,7 @@ ${rejected
|
|||||||
ha-assist-chip {
|
ha-assist-chip {
|
||||||
--ha-assist-chip-container-shape: 10px;
|
--ha-assist-chip-container-shape: 10px;
|
||||||
}
|
}
|
||||||
ha-button-menu-new ha-assist-chip {
|
ha-md-button-menu ha-assist-chip {
|
||||||
--md-assist-chip-trailing-space: 8px;
|
--md-assist-chip-trailing-space: 8px;
|
||||||
}
|
}
|
||||||
ha-label {
|
ha-label {
|
||||||
|
@ -59,7 +59,7 @@ import "../../../components/ha-filter-floor-areas";
|
|||||||
import "../../../components/ha-filter-labels";
|
import "../../../components/ha-filter-labels";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-icon-overflow-menu";
|
import "../../../components/ha-icon-overflow-menu";
|
||||||
import "../../../components/ha-menu-item";
|
import "../../../components/ha-md-menu-item";
|
||||||
import "../../../components/ha-sub-menu";
|
import "../../../components/ha-sub-menu";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
import { createAreaRegistryEntry } from "../../../data/area_registry";
|
||||||
@ -413,7 +413,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const categoryItems = html`${this._categories?.map(
|
const categoryItems = html`${this._categories?.map(
|
||||||
(category) =>
|
(category) =>
|
||||||
html`<ha-menu-item
|
html`<ha-md-menu-item
|
||||||
.value=${category.category_id}
|
.value=${category.category_id}
|
||||||
@click=${this._handleBulkCategory}
|
@click=${this._handleBulkCategory}
|
||||||
>
|
>
|
||||||
@ -421,20 +421,20 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
|
? html`<ha-icon slot="start" .icon=${category.icon}></ha-icon>`
|
||||||
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
|
: html`<ha-svg-icon slot="start" .path=${mdiTag}></ha-svg-icon>`}
|
||||||
<div slot="headline">${category.name}</div>
|
<div slot="headline">${category.name}</div>
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
)}
|
)}
|
||||||
<ha-menu-item .value=${null} @click=${this._handleBulkCategory}>
|
<ha-md-menu-item .value=${null} @click=${this._handleBulkCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.no_category"
|
"ui.panel.config.automation.picker.bulk_actions.no_category"
|
||||||
)}
|
)}
|
||||||
</div> </ha-menu-item
|
</div> </ha-md-menu-item
|
||||||
><md-divider role="separator" tabindex="-1"></md-divider>
|
><md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateCategory}>
|
<ha-md-menu-item @click=${this._bulkCreateCategory}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.category.editor.add")}
|
${this.hass.localize("ui.panel.config.category.editor.add")}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
|
|
||||||
const labelItems = html`${this._labels?.map((label) => {
|
const labelItems = html`${this._labels?.map((label) => {
|
||||||
const color = label.color ? computeCssColor(label.color) : undefined;
|
const color = label.color ? computeCssColor(label.color) : undefined;
|
||||||
@ -446,7 +446,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
this._selected.some((entityId) =>
|
this._selected.some((entityId) =>
|
||||||
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
this.hass.entities[entityId]?.labels.includes(label.label_id)
|
||||||
);
|
);
|
||||||
return html`<ha-menu-item
|
return html`<ha-md-menu-item
|
||||||
.value=${label.label_id}
|
.value=${label.label_id}
|
||||||
.action=${selected ? "remove" : "add"}
|
.action=${selected ? "remove" : "add"}
|
||||||
@click=${this._handleBulkLabel}
|
@click=${this._handleBulkLabel}
|
||||||
@ -464,18 +464,18 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
: nothing}
|
: nothing}
|
||||||
${label.name}
|
${label.name}
|
||||||
</ha-label>
|
</ha-label>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
})}
|
})}
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateLabel}>
|
<ha-md-menu-item @click=${this._bulkCreateLabel}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize("ui.panel.config.labels.add_label")}
|
${this.hass.localize("ui.panel.config.labels.add_label")}
|
||||||
</div></ha-menu-item
|
</div></ha-md-menu-item
|
||||||
>`;
|
>`;
|
||||||
|
|
||||||
const areaItems = html`${Object.values(this.hass.areas).map(
|
const areaItems = html`${Object.values(this.hass.areas).map(
|
||||||
(area) =>
|
(area) =>
|
||||||
html`<ha-menu-item
|
html`<ha-md-menu-item
|
||||||
.value=${area.area_id}
|
.value=${area.area_id}
|
||||||
@click=${this._handleBulkArea}
|
@click=${this._handleBulkArea}
|
||||||
>
|
>
|
||||||
@ -486,23 +486,23 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
.path=${mdiTextureBox}
|
.path=${mdiTextureBox}
|
||||||
></ha-svg-icon>`}
|
></ha-svg-icon>`}
|
||||||
<div slot="headline">${area.name}</div>
|
<div slot="headline">${area.name}</div>
|
||||||
</ha-menu-item>`
|
</ha-md-menu-item>`
|
||||||
)}
|
)}
|
||||||
<ha-menu-item .value=${null} @click=${this._handleBulkArea}>
|
<ha-md-menu-item .value=${null} @click=${this._handleBulkArea}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
"ui.panel.config.devices.picker.bulk_actions.no_area"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<md-divider role="separator" tabindex="-1"></md-divider>
|
<md-divider role="separator" tabindex="-1"></md-divider>
|
||||||
<ha-menu-item @click=${this._bulkCreateArea}>
|
<ha-md-menu-item @click=${this._bulkCreateArea}>
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
"ui.panel.config.devices.picker.bulk_actions.add_area"
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ha-menu-item>`;
|
</ha-md-menu-item>`;
|
||||||
|
|
||||||
const areasInOverflow =
|
const areasInOverflow =
|
||||||
(this._sizeController.value && this._sizeController.value < 900) ||
|
(this._sizeController.value && this._sizeController.value < 900) ||
|
||||||
@ -635,7 +635,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-filter-blueprints>
|
></ha-filter-blueprints>
|
||||||
|
|
||||||
${!this.narrow
|
${!this.narrow
|
||||||
? html`<ha-button-menu-new slot="selection-bar">
|
? html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -648,10 +648,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${categoryItems}
|
${categoryItems}
|
||||||
</ha-button-menu-new>
|
</ha-md-button-menu>
|
||||||
${labelsInOverflow
|
${labelsInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -664,10 +664,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${labelItems}
|
${labelItems}
|
||||||
</ha-button-menu-new>`}
|
</ha-md-button-menu>`}
|
||||||
${areasInOverflow
|
${areasInOverflow
|
||||||
? nothing
|
? nothing
|
||||||
: html`<ha-button-menu-new slot="selection-bar">
|
: html`<ha-md-button-menu slot="selection-bar">
|
||||||
<ha-assist-chip
|
<ha-assist-chip
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
@ -680,11 +680,11 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-assist-chip>
|
</ha-assist-chip>
|
||||||
${areaItems}
|
${areaItems}
|
||||||
</ha-button-menu-new>`}`
|
</ha-md-button-menu>`}`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this.narrow || areasInOverflow
|
${this.narrow || areasInOverflow
|
||||||
? html`
|
? html`
|
||||||
<ha-button-menu-new has-overflow slot="selection-bar">
|
<ha-md-button-menu has-overflow slot="selection-bar">
|
||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-assist-chip
|
? html`<ha-assist-chip
|
||||||
@ -712,7 +712,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow
|
this.narrow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.move_category"
|
"ui.panel.config.automation.picker.bulk_actions.move_category"
|
||||||
@ -722,7 +722,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${categoryItems}</ha-menu>
|
<ha-menu slot="menu">${categoryItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
@ -730,7 +730,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow || labelsInOverflow
|
this.narrow || labelsInOverflow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
"ui.panel.config.automation.picker.bulk_actions.add_label"
|
||||||
@ -740,7 +740,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${labelItems}</ha-menu>
|
<ha-menu slot="menu">${labelItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
@ -748,7 +748,7 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
${
|
${
|
||||||
this.narrow || areasInOverflow
|
this.narrow || areasInOverflow
|
||||||
? html`<ha-sub-menu>
|
? html`<ha-sub-menu>
|
||||||
<ha-menu-item slot="item">
|
<ha-md-menu-item slot="item">
|
||||||
<div slot="headline">
|
<div slot="headline">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
"ui.panel.config.devices.picker.bulk_actions.move_area"
|
||||||
@ -758,12 +758,12 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
slot="end"
|
slot="end"
|
||||||
.path=${mdiChevronRight}
|
.path=${mdiChevronRight}
|
||||||
></ha-svg-icon>
|
></ha-svg-icon>
|
||||||
</ha-menu-item>
|
</ha-md-menu-item>
|
||||||
<ha-menu slot="menu">${areaItems}</ha-menu>
|
<ha-menu slot="menu">${areaItems}</ha-menu>
|
||||||
</ha-sub-menu>`
|
</ha-sub-menu>`
|
||||||
: nothing
|
: nothing
|
||||||
}
|
}
|
||||||
</ha-button-menu-new>`
|
</ha-md-button-menu>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${!this.scripts.length
|
${!this.scripts.length
|
||||||
? html` <div class="empty" slot="empty">
|
? html` <div class="empty" slot="empty">
|
||||||
@ -1295,7 +1295,7 @@ ${rejected
|
|||||||
ha-assist-chip {
|
ha-assist-chip {
|
||||||
--ha-assist-chip-container-shape: 10px;
|
--ha-assist-chip-container-shape: 10px;
|
||||||
}
|
}
|
||||||
ha-button-menu-new ha-assist-chip {
|
ha-md-button-menu ha-assist-chip {
|
||||||
--md-assist-chip-trailing-space: 8px;
|
--md-assist-chip-trailing-space: 8px;
|
||||||
}
|
}
|
||||||
ha-label {
|
ha-label {
|
||||||
|
@ -3,7 +3,6 @@ import { HassEntity } from "home-assistant-js-websocket";
|
|||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { computeCssColor } from "../../../common/color/compute-color";
|
import { computeCssColor } from "../../../common/color/compute-color";
|
||||||
@ -12,6 +11,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
|
|||||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
import { stateActive } from "../../../common/entity/state_active";
|
import { stateActive } from "../../../common/entity/state_active";
|
||||||
import { stateColorCss } from "../../../common/entity/state_color";
|
import { stateColorCss } from "../../../common/entity/state_color";
|
||||||
|
import "../../../components/ha-badge";
|
||||||
import "../../../components/ha-ripple";
|
import "../../../components/ha-ripple";
|
||||||
import "../../../components/ha-state-icon";
|
import "../../../components/ha-state-icon";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
@ -160,15 +160,14 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
|||||||
|
|
||||||
if (!stateObj) {
|
if (!stateObj) {
|
||||||
return html`
|
return html`
|
||||||
<div class="badge error">
|
<ha-badge .label=${entityId} class="error">
|
||||||
<ha-svg-icon .hass=${this.hass} .path=${mdiAlertCircle}></ha-svg-icon>
|
<ha-svg-icon
|
||||||
<span class="info">
|
slot="icon"
|
||||||
<span class="label">${entityId}</span>
|
.hass=${this.hass}
|
||||||
<span class="content">
|
.path=${mdiAlertCircle}
|
||||||
|
></ha-svg-icon>
|
||||||
${this.hass.localize("ui.badge.entity.not_found")}
|
${this.hass.localize("ui.badge.entity.not_found")}
|
||||||
</span>
|
</ha-badge>
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,42 +203,32 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
|||||||
const content = showState ? stateDisplay : showName ? name : undefined;
|
const content = showState ? stateDisplay : showName ? name : undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<ha-badge
|
||||||
style=${styleMap(style)}
|
.type=${this.hasAction ? "button" : "badge"}
|
||||||
class="badge ${classMap({
|
|
||||||
active,
|
|
||||||
"no-info": !showState && !showName,
|
|
||||||
"no-icon": !showIcon,
|
|
||||||
})}"
|
|
||||||
@action=${this._handleAction}
|
@action=${this._handleAction}
|
||||||
.actionHandler=${actionHandler({
|
.actionHandler=${actionHandler({
|
||||||
hasHold: hasAction(this._config!.hold_action),
|
hasHold: hasAction(this._config!.hold_action),
|
||||||
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
hasDoubleClick: hasAction(this._config!.double_tap_action),
|
||||||
})}
|
})}
|
||||||
role=${ifDefined(this.hasAction ? "button" : undefined)}
|
.label=${label}
|
||||||
tabindex=${ifDefined(this.hasAction ? "0" : undefined)}
|
.iconOnly=${!content}
|
||||||
|
style=${styleMap(style)}
|
||||||
|
class=${classMap({ active })}
|
||||||
>
|
>
|
||||||
<ha-ripple .disabled=${!this.hasAction}></ha-ripple>
|
|
||||||
${showIcon
|
${showIcon
|
||||||
? imageUrl
|
? imageUrl
|
||||||
? html`<img src=${imageUrl} aria-hidden />`
|
? html`<img slot="icon" src=${imageUrl} aria-hidden />`
|
||||||
: html`
|
: html`
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
|
slot="icon"
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.icon=${this._config.icon}
|
.icon=${this._config.icon}
|
||||||
></ha-state-icon>
|
></ha-state-icon>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
${content
|
${content}
|
||||||
? html`
|
</ha-badge>
|
||||||
<span class="info">
|
|
||||||
${label ? html`<span class="label">${name}</span>` : nothing}
|
|
||||||
<span class="content">${content}</span>
|
|
||||||
</span>
|
|
||||||
`
|
|
||||||
: nothing}
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,119 +238,15 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
|
|||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
ha-badge {
|
||||||
--badge-color: var(--state-inactive-color);
|
--badge-color: var(--state-inactive-color);
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
}
|
||||||
.badge.error {
|
ha-badge.error {
|
||||||
--badge-color: var(--red-color);
|
--badge-color: var(--red-color);
|
||||||
}
|
}
|
||||||
.badge {
|
ha-badge.active {
|
||||||
position: relative;
|
|
||||||
--ha-ripple-color: var(--badge-color);
|
|
||||||
--ha-ripple-hover-opacity: 0.04;
|
|
||||||
--ha-ripple-pressed-opacity: 0.12;
|
|
||||||
transition:
|
|
||||||
box-shadow 180ms ease-in-out,
|
|
||||||
border-color 180ms ease-in-out;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 8px;
|
|
||||||
height: var(--ha-badge-size, 36px);
|
|
||||||
min-width: var(--ha-badge-size, 36px);
|
|
||||||
padding: 0px 8px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: auto;
|
|
||||||
border-radius: var(
|
|
||||||
--ha-badge-border-radius,
|
|
||||||
calc(var(--ha-badge-size, 36px) / 2)
|
|
||||||
);
|
|
||||||
background: var(
|
|
||||||
--ha-card-background,
|
|
||||||
var(--card-background-color, white)
|
|
||||||
);
|
|
||||||
-webkit-backdrop-filter: var(--ha-card-backdrop-filter, none);
|
|
||||||
backdrop-filter: var(--ha-card-backdrop-filter, none);
|
|
||||||
border-width: var(--ha-card-border-width, 1px);
|
|
||||||
box-shadow: var(--ha-card-box-shadow, none);
|
|
||||||
border-style: solid;
|
|
||||||
border-color: var(
|
|
||||||
--ha-card-border-color,
|
|
||||||
var(--divider-color, #e0e0e0)
|
|
||||||
);
|
|
||||||
--mdc-icon-size: 18px;
|
|
||||||
text-align: center;
|
|
||||||
font-family: Roboto;
|
|
||||||
}
|
|
||||||
.badge:focus-visible {
|
|
||||||
--shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent);
|
|
||||||
--shadow-focus: 0 0 0 1px var(--badge-color);
|
|
||||||
border-color: var(--badge-color);
|
|
||||||
box-shadow: var(--shadow-default), var(--shadow-focus);
|
|
||||||
}
|
|
||||||
button,
|
|
||||||
[role="button"] {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
button:focus,
|
|
||||||
[role="button"]:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
.badge.active {
|
|
||||||
--badge-color: var(--primary-color);
|
--badge-color: var(--primary-color);
|
||||||
}
|
}
|
||||||
.info {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
padding-right: 4px;
|
|
||||||
padding-inline-end: 4px;
|
|
||||||
padding-inline-start: initial;
|
|
||||||
}
|
|
||||||
.label {
|
|
||||||
font-size: 10px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 10px;
|
|
||||||
letter-spacing: 0.1px;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
.content {
|
|
||||||
font-size: 12px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 16px;
|
|
||||||
letter-spacing: 0.1px;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
ha-state-icon,
|
|
||||||
ha-svg-icon {
|
|
||||||
color: var(--badge-color);
|
|
||||||
line-height: 0;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
|
||||||
object-fit: cover;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.badge.no-info {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.badge:not(.no-icon):not(.no-info) img {
|
|
||||||
margin-left: -6px;
|
|
||||||
margin-inline-start: -6px;
|
|
||||||
margin-inline-end: initial;
|
|
||||||
}
|
|
||||||
.badge.no-icon .info {
|
|
||||||
padding-right: 4px;
|
|
||||||
padding-left: 4px;
|
|
||||||
padding-inline-end: 4px;
|
|
||||||
padding-inline-start: 4px;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,11 @@ import { mdiAlertCircle } from "@mdi/js";
|
|||||||
import { dump } from "js-yaml";
|
import { dump } from "js-yaml";
|
||||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import "../../../components/ha-label-badge";
|
import "../../../components/ha-badge";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
import { showAlertDialog } from "../custom-card-helpers";
|
import { showAlertDialog } from "../custom-card-helpers";
|
||||||
import { LovelaceBadge } from "../types";
|
import { LovelaceBadge } from "../types";
|
||||||
import { HuiEntityBadge } from "./hui-entity-badge";
|
|
||||||
import { ErrorBadgeConfig } from "./types";
|
import { ErrorBadgeConfig } from "./types";
|
||||||
|
|
||||||
export const createErrorBadgeElement = (config) => {
|
export const createErrorBadgeElement = (config) => {
|
||||||
@ -55,29 +54,25 @@ export class HuiErrorBadge extends LitElement implements LovelaceBadge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<button class="badge error" @click=${this._viewDetail}>
|
<ha-badge
|
||||||
<ha-svg-icon .hass=${this.hass} .path=${mdiAlertCircle}></ha-svg-icon>
|
class="error"
|
||||||
<ha-ripple></ha-ripple>
|
@click=${this._viewDetail}
|
||||||
<span class="content">
|
type="button"
|
||||||
<span class="name">Error</span>
|
label="Error"
|
||||||
<span class="state">${this._config.error}</span>
|
>
|
||||||
</span>
|
<ha-svg-icon slot="icon" .path=${mdiAlertCircle}></ha-svg-icon>
|
||||||
</button>
|
<div class="content">${this._config.error}</div>
|
||||||
|
</ha-badge>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return css`
|
||||||
HuiEntityBadge.styles,
|
ha-badge {
|
||||||
css`
|
|
||||||
.badge.error {
|
|
||||||
--badge-color: var(--error-color);
|
--badge-color: var(--error-color);
|
||||||
border-color: var(--badge-color);
|
--ha-card-border-color: var(--error-color);
|
||||||
}
|
}
|
||||||
ha-svg-icon {
|
.content {
|
||||||
color: var(--badge-color);
|
|
||||||
}
|
|
||||||
.state {
|
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
@ -88,8 +83,7 @@ export class HuiErrorBadge extends LitElement implements LovelaceBadge {
|
|||||||
white-space: break-spaces;
|
white-space: break-spaces;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
}
|
}
|
||||||
`,
|
`;
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
260
src/panels/lovelace/cards/hui-heading-card.ts
Normal file
260
src/panels/lovelace/cards/hui-heading-card.ts
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-icon";
|
||||||
|
import "../../../components/ha-icon-next";
|
||||||
|
import "../../../components/ha-state-icon";
|
||||||
|
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
|
||||||
|
import "../../../state-display/state-display";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { actionHandler } from "../common/directives/action-handler-directive";
|
||||||
|
import { handleAction } from "../common/handle-action";
|
||||||
|
import { hasAction } from "../common/has-action";
|
||||||
|
import type {
|
||||||
|
LovelaceCard,
|
||||||
|
LovelaceCardEditor,
|
||||||
|
LovelaceLayoutOptions,
|
||||||
|
} from "../types";
|
||||||
|
import type { HeadingCardConfig, HeadingCardEntityConfig } from "./types";
|
||||||
|
|
||||||
|
@customElement("hui-heading-card")
|
||||||
|
export class HuiHeadingCard extends LitElement implements LovelaceCard {
|
||||||
|
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||||
|
await import("../editor/config-elements/hui-heading-card-editor");
|
||||||
|
return document.createElement("hui-heading-card-editor");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getStubConfig(hass: HomeAssistant): HeadingCardConfig {
|
||||||
|
return {
|
||||||
|
type: "heading",
|
||||||
|
icon: "mdi:fridge",
|
||||||
|
heading: hass.localize("ui.panel.lovelace.cards.heading.default_heading"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _config?: HeadingCardConfig;
|
||||||
|
|
||||||
|
public setConfig(config: HeadingCardConfig): void {
|
||||||
|
this._config = {
|
||||||
|
tap_action: {
|
||||||
|
action: "none",
|
||||||
|
},
|
||||||
|
...config,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public getCardSize(): number {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLayoutOptions(): LovelaceLayoutOptions {
|
||||||
|
return {
|
||||||
|
grid_columns: "full",
|
||||||
|
grid_rows: this._config?.heading_style === "subtitle" ? "auto" : 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleAction(ev: ActionHandlerEvent) {
|
||||||
|
handleAction(this, this.hass!, this._config!, ev.detail.action!);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionable = hasAction(this._config.tap_action);
|
||||||
|
|
||||||
|
const style = this._config.heading_style || "title";
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="container">
|
||||||
|
<div
|
||||||
|
class="content ${style}"
|
||||||
|
@action=${this._handleAction}
|
||||||
|
.actionHandler=${actionHandler()}
|
||||||
|
role=${ifDefined(actionable ? "button" : undefined)}
|
||||||
|
tabindex=${ifDefined(actionable ? "0" : undefined)}
|
||||||
|
>
|
||||||
|
${this._config.icon
|
||||||
|
? html`<ha-icon .icon=${this._config.icon}></ha-icon>`
|
||||||
|
: nothing}
|
||||||
|
${this._config.heading
|
||||||
|
? html`<p>${this._config.heading}</p>`
|
||||||
|
: nothing}
|
||||||
|
${actionable ? html`<ha-icon-next></ha-icon-next>` : nothing}
|
||||||
|
</div>
|
||||||
|
${this._config.entities?.length
|
||||||
|
? html`
|
||||||
|
<div class="entities">
|
||||||
|
${this._config.entities.map((config) =>
|
||||||
|
this._renderEntity(config)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleEntityAction(ev: ActionHandlerEvent) {
|
||||||
|
const config = {
|
||||||
|
tap_action: {
|
||||||
|
action: "none",
|
||||||
|
},
|
||||||
|
...(ev.currentTarget as any).config,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleAction(this, this.hass!, config, ev.detail.action!);
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderEntity(entityConfig: string | HeadingCardEntityConfig) {
|
||||||
|
const config =
|
||||||
|
typeof entityConfig === "string"
|
||||||
|
? { entity: entityConfig }
|
||||||
|
: entityConfig;
|
||||||
|
|
||||||
|
const stateObj = this.hass!.states[config.entity];
|
||||||
|
|
||||||
|
if (!stateObj) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actionable = hasAction(config.tap_action || { action: "none" });
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
.config=${config}
|
||||||
|
class="entity"
|
||||||
|
@action=${this._handleEntityAction}
|
||||||
|
.actionHandler=${actionHandler()}
|
||||||
|
role=${ifDefined(actionable ? "button" : undefined)}
|
||||||
|
tabindex=${ifDefined(actionable ? "0" : undefined)}
|
||||||
|
>
|
||||||
|
<ha-state-icon
|
||||||
|
.hass=${this.hass}
|
||||||
|
.icon=${config.icon}
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
></ha-state-icon>
|
||||||
|
<state-display
|
||||||
|
.hass=${this.hass}
|
||||||
|
.stateObj=${stateObj}
|
||||||
|
.content=${config.content || "state"}
|
||||||
|
></state-display>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
ha-card {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
[role="button"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
ha-icon-next {
|
||||||
|
display: inline-block;
|
||||||
|
transition: transform 180ms ease-in-out;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 2px 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.content:hover ha-icon-next {
|
||||||
|
transform: translateX(calc(4px * var(--scale-direction)));
|
||||||
|
}
|
||||||
|
.container .content {
|
||||||
|
flex: 1 0 fill;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
.container .content:not(:has(p)) {
|
||||||
|
min-width: fit-content;
|
||||||
|
}
|
||||||
|
.container .entities {
|
||||||
|
flex: 0 0;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 24px;
|
||||||
|
letter-spacing: 0.1px;
|
||||||
|
--mdc-icon-size: 16px;
|
||||||
|
}
|
||||||
|
.content ha-icon,
|
||||||
|
.content ha-icon-next {
|
||||||
|
display: flex;
|
||||||
|
flex: none;
|
||||||
|
}
|
||||||
|
.content p {
|
||||||
|
margin: 0;
|
||||||
|
font-family: Roboto;
|
||||||
|
font-style: normal;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex-shrink: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.content.subtitle {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
.entities {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 4px 10px;
|
||||||
|
}
|
||||||
|
.entities .entity {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
white-space: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
font-family: Roboto;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 20px; /* 142.857% */
|
||||||
|
letter-spacing: 0.1px;
|
||||||
|
--mdc-icon-size: 14px;
|
||||||
|
}
|
||||||
|
.entities .entity ha-state-icon {
|
||||||
|
--ha-icon-display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-heading-card": HuiHeadingCard;
|
||||||
|
}
|
||||||
|
}
|
@ -6,14 +6,13 @@ import {
|
|||||||
html,
|
html,
|
||||||
nothing,
|
nothing,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { mdiChevronRight } from "@mdi/js";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
import "../../../components/chart/state-history-charts";
|
import "../../../components/chart/state-history-charts";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-next";
|
||||||
import {
|
import {
|
||||||
HistoryResult,
|
HistoryResult,
|
||||||
computeHistory,
|
computeHistory,
|
||||||
@ -209,9 +208,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
? html`
|
? html`
|
||||||
<h1 class="card-header">
|
<h1 class="card-header">
|
||||||
${this._config.title}
|
${this._config.title}
|
||||||
<a href=${configUrl}
|
<a href=${configUrl}><ha-icon-next></ha-icon-next></a>
|
||||||
><ha-icon-button .path=${mdiChevronRight}></ha-icon-button
|
|
||||||
></a>
|
|
||||||
</h1>
|
</h1>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
@ -258,7 +255,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.card-header ha-icon-button {
|
.card-header ha-icon-next {
|
||||||
--mdc-icon-button-size: 24px;
|
--mdc-icon-button-size: 24px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
|
@ -136,8 +136,10 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
const config = {
|
const config = {
|
||||||
entity: this._config!.entity,
|
entity: this._config!.entity,
|
||||||
tap_action: this._config!.icon_tap_action,
|
tap_action: this._config!.icon_tap_action,
|
||||||
|
hold_action: this._config!.icon_hold_action,
|
||||||
|
double_tap_action: this._config!.icon_double_tap_action,
|
||||||
};
|
};
|
||||||
handleAction(this, this.hass!, config, "tap");
|
handleAction(this, this.hass!, config, ev.detail.action!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getImageUrl(entity: HassEntity): string | undefined {
|
private _getImageUrl(entity: HassEntity): string | undefined {
|
||||||
@ -286,7 +288,10 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
|
|||||||
role=${ifDefined(this.hasIconAction ? "button" : undefined)}
|
role=${ifDefined(this.hasIconAction ? "button" : undefined)}
|
||||||
tabindex=${ifDefined(this.hasIconAction ? "0" : undefined)}
|
tabindex=${ifDefined(this.hasIconAction ? "0" : undefined)}
|
||||||
@action=${this._handleIconAction}
|
@action=${this._handleIconAction}
|
||||||
.actionHandler=${actionHandler()}
|
.actionHandler=${actionHandler({
|
||||||
|
hasHold: hasAction(this._config!.icon_hold_action),
|
||||||
|
hasDoubleClick: hasAction(this._config!.icon_double_tap_action),
|
||||||
|
})}
|
||||||
>
|
>
|
||||||
${imageUrl
|
${imageUrl
|
||||||
? html`
|
? html`
|
||||||
|
@ -498,5 +498,22 @@ export interface TileCardConfig extends LovelaceCardConfig {
|
|||||||
hold_action?: ActionConfig;
|
hold_action?: ActionConfig;
|
||||||
double_tap_action?: ActionConfig;
|
double_tap_action?: ActionConfig;
|
||||||
icon_tap_action?: ActionConfig;
|
icon_tap_action?: ActionConfig;
|
||||||
|
icon_hold_action?: ActionConfig;
|
||||||
|
icon_double_tap_action?: ActionConfig;
|
||||||
features?: LovelaceCardFeatureConfig[];
|
features?: LovelaceCardFeatureConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface HeadingCardEntityConfig {
|
||||||
|
entity: string;
|
||||||
|
content?: string | string[];
|
||||||
|
icon?: string;
|
||||||
|
tap_action?: ActionConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HeadingCardConfig extends LovelaceCardConfig {
|
||||||
|
heading_style?: "title" | "subtitle";
|
||||||
|
heading?: string;
|
||||||
|
icon?: string;
|
||||||
|
tap_action?: ActionConfig;
|
||||||
|
entities?: (string | HeadingCardEntityConfig)[];
|
||||||
|
}
|
||||||
|
@ -275,9 +275,9 @@ export class HuiCardEditMode extends LitElement {
|
|||||||
position: relative;
|
position: relative;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 12px;
|
padding: 8px;
|
||||||
background: var(--secondary-background-color);
|
background: var(--secondary-background-color);
|
||||||
--mdc-icon-size: 24px;
|
--mdc-icon-size: 20px;
|
||||||
}
|
}
|
||||||
.more {
|
.more {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -10,6 +10,7 @@ import "../cards/hui-sensor-card";
|
|||||||
import "../cards/hui-thermostat-card";
|
import "../cards/hui-thermostat-card";
|
||||||
import "../cards/hui-weather-forecast-card";
|
import "../cards/hui-weather-forecast-card";
|
||||||
import "../cards/hui-tile-card";
|
import "../cards/hui-tile-card";
|
||||||
|
import "../cards/hui-heading-card";
|
||||||
import {
|
import {
|
||||||
createLovelaceElement,
|
createLovelaceElement,
|
||||||
getLovelaceElementClass,
|
getLovelaceElementClass,
|
||||||
@ -29,6 +30,7 @@ const ALWAYS_LOADED_TYPES = new Set([
|
|||||||
"thermostat",
|
"thermostat",
|
||||||
"weather-forecast",
|
"weather-forecast",
|
||||||
"tile",
|
"tile",
|
||||||
|
"heading",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const LAZY_LOAD_TYPES = {
|
const LAZY_LOAD_TYPES = {
|
||||||
|
@ -34,4 +34,21 @@ export const configElementStyle = css`
|
|||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
ha-expansion-panel {
|
||||||
|
display: block;
|
||||||
|
--expansion-panel-content-padding: 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
--ha-card-border-radius: 6px;
|
||||||
|
}
|
||||||
|
ha-expansion-panel .content {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
ha-expansion-panel > * {
|
||||||
|
margin: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
ha-expansion-panel ha-svg-icon {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { mdiDelete, mdiDrag, mdiListBox, mdiPencil, mdiPlus } from "@mdi/js";
|
import { mdiDelete, mdiDrag, mdiPencil, mdiPlus } from "@mdi/js";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
|
||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-icon-button";
|
import "../../../../components/ha-icon-button";
|
||||||
import "../../../../components/ha-list-item";
|
import "../../../../components/ha-list-item";
|
||||||
@ -236,12 +235,6 @@ export class HuiCardFeaturesEditor extends LitElement {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-expansion-panel outlined>
|
|
||||||
<h3 slot="header">
|
|
||||||
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
|
|
||||||
${this.hass!.localize("ui.panel.lovelace.editor.features.name")}
|
|
||||||
</h3>
|
|
||||||
<div class="content">
|
|
||||||
${supportedFeaturesType.length === 0 && this.features.length === 0
|
${supportedFeaturesType.length === 0 && this.features.length === 0
|
||||||
? html`
|
? html`
|
||||||
<ha-alert type="info">
|
<ha-alert type="info">
|
||||||
@ -251,10 +244,7 @@ export class HuiCardFeaturesEditor extends LitElement {
|
|||||||
</ha-alert>
|
</ha-alert>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
<ha-sortable
|
<ha-sortable handle-selector=".handle" @item-moved=${this._featureMoved}>
|
||||||
handle-selector=".handle"
|
|
||||||
@item-moved=${this._featureMoved}
|
|
||||||
>
|
|
||||||
<div class="features">
|
<div class="features">
|
||||||
${repeat(
|
${repeat(
|
||||||
this.features,
|
this.features,
|
||||||
@ -347,8 +337,6 @@ export class HuiCardFeaturesEditor extends LitElement {
|
|||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
|
||||||
</ha-expansion-panel>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -409,23 +397,6 @@ export class HuiCardFeaturesEditor extends LitElement {
|
|||||||
display: flex !important;
|
display: flex !important;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.content {
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
ha-expansion-panel {
|
|
||||||
display: block;
|
|
||||||
--expansion-panel-content-padding: 0;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
ha-svg-icon,
|
|
||||||
ha-icon {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
ha-button-menu {
|
ha-button-menu {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,296 @@
|
|||||||
|
import { mdiDelete, mdiDrag, mdiPencil, mdiPlus } from "@mdi/js";
|
||||||
|
import { ComboBoxLightOpenedChangedEvent } from "@vaadin/combo-box/vaadin-combo-box-light";
|
||||||
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
|
import { repeat } from "lit/directives/repeat";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { preventDefault } from "../../../../common/dom/prevent_default";
|
||||||
|
import { stopPropagation } from "../../../../common/dom/stop_propagation";
|
||||||
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
|
import type { HaEntityPicker } from "../../../../components/entity/ha-entity-picker";
|
||||||
|
import "../../../../components/ha-button";
|
||||||
|
import "../../../../components/ha-icon-button";
|
||||||
|
import "../../../../components/ha-list-item";
|
||||||
|
import "../../../../components/ha-sortable";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
|
type EntityConfig = {
|
||||||
|
entity: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"edit-entity": { index: number };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("hui-entities-editor")
|
||||||
|
export class HuiEntitiesEditor extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property({ attribute: false })
|
||||||
|
public entities?: EntityConfig[];
|
||||||
|
|
||||||
|
@query(".add-container", true) private _addContainer?: HTMLDivElement;
|
||||||
|
|
||||||
|
@query("ha-entity-picker") private _entityPicker?: HaEntityPicker;
|
||||||
|
|
||||||
|
@state() private _addMode = false;
|
||||||
|
|
||||||
|
private _opened = false;
|
||||||
|
|
||||||
|
private _entitiesKeys = new WeakMap<EntityConfig, string>();
|
||||||
|
|
||||||
|
private _getKey(entity: EntityConfig) {
|
||||||
|
if (!this._entitiesKeys.has(entity)) {
|
||||||
|
this._entitiesKeys.set(entity, Math.random().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._entitiesKeys.get(entity)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this.hass) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
${this.entities
|
||||||
|
? html`
|
||||||
|
<ha-sortable
|
||||||
|
handle-selector=".handle"
|
||||||
|
@item-moved=${this._entityMoved}
|
||||||
|
>
|
||||||
|
<div class="entities">
|
||||||
|
${repeat(
|
||||||
|
this.entities,
|
||||||
|
(entityConf) => this._getKey(entityConf),
|
||||||
|
(entityConf, index) => {
|
||||||
|
const editable = true;
|
||||||
|
|
||||||
|
const entityId = entityConf.entity;
|
||||||
|
const stateObj = this.hass.states[entityId];
|
||||||
|
const name = stateObj
|
||||||
|
? stateObj.attributes.friendly_name
|
||||||
|
: undefined;
|
||||||
|
return html`
|
||||||
|
<div class="entity">
|
||||||
|
<div class="handle">
|
||||||
|
<ha-svg-icon .path=${mdiDrag}></ha-svg-icon>
|
||||||
|
</div>
|
||||||
|
<div class="entity-content">
|
||||||
|
<span>${name || entityId}</span>
|
||||||
|
</div>
|
||||||
|
${editable
|
||||||
|
? html`
|
||||||
|
<ha-icon-button
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.entities.edit`
|
||||||
|
)}
|
||||||
|
.path=${mdiPencil}
|
||||||
|
class="edit-icon"
|
||||||
|
.index=${index}
|
||||||
|
@click=${this._editEntity}
|
||||||
|
.disabled=${!editable}
|
||||||
|
></ha-icon-button>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
<ha-icon-button
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.entities.remove`
|
||||||
|
)}
|
||||||
|
.path=${mdiDelete}
|
||||||
|
class="remove-icon"
|
||||||
|
.index=${index}
|
||||||
|
@click=${this._removeEntity}
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</ha-sortable>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
<div class="add-container">
|
||||||
|
<ha-button
|
||||||
|
data-add-entity
|
||||||
|
outlined
|
||||||
|
.label=${this.hass!.localize(`ui.panel.lovelace.editor.entities.add`)}
|
||||||
|
@click=${this._addEntity}
|
||||||
|
>
|
||||||
|
<ha-svg-icon .path=${mdiPlus} slot="icon"></ha-svg-icon>
|
||||||
|
</ha-button>
|
||||||
|
${this._renderPicker()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderPicker() {
|
||||||
|
if (!this._addMode) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<mwc-menu-surface
|
||||||
|
open
|
||||||
|
.anchor=${this._addContainer}
|
||||||
|
@closed=${this._onClosed}
|
||||||
|
@opened=${this._onOpened}
|
||||||
|
@opened-changed=${this._openedChanged}
|
||||||
|
@input=${stopPropagation}
|
||||||
|
>
|
||||||
|
<ha-entity-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
id="input"
|
||||||
|
.type=${"entity_id"}
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.components.target-picker.add_entity_id"
|
||||||
|
)}
|
||||||
|
@value-changed=${this._entityPicked}
|
||||||
|
@click=${preventDefault}
|
||||||
|
allow-custom-entity
|
||||||
|
></ha-entity-picker>
|
||||||
|
</mwc-menu-surface>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onClosed(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.target.open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _onOpened() {
|
||||||
|
if (!this._addMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this._entityPicker?.focus();
|
||||||
|
await this._entityPicker?.open();
|
||||||
|
this._opened = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openedChanged(ev: ComboBoxLightOpenedChangedEvent) {
|
||||||
|
if (this._opened && !ev.detail.value) {
|
||||||
|
this._opened = false;
|
||||||
|
this._addMode = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _addEntity(ev): Promise<void> {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._addMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityPicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!ev.detail.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newEntity: EntityConfig = { entity: ev.detail.value };
|
||||||
|
const newEntities = (this.entities || []).concat(newEntity);
|
||||||
|
fireEvent(this, "entities-changed", { entities: newEntities });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityMoved(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const { oldIndex, newIndex } = ev.detail;
|
||||||
|
|
||||||
|
const newEntities = (this.entities || []).concat();
|
||||||
|
|
||||||
|
newEntities.splice(newIndex, 0, newEntities.splice(oldIndex, 1)[0]);
|
||||||
|
|
||||||
|
fireEvent(this, "entities-changed", { entities: newEntities });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeEntity(ev: CustomEvent): void {
|
||||||
|
const index = (ev.currentTarget as any).index;
|
||||||
|
const newEntities = (this.entities || []).concat();
|
||||||
|
|
||||||
|
newEntities.splice(index, 1);
|
||||||
|
|
||||||
|
fireEvent(this, "entities-changed", { entities: newEntities });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _editEntity(ev: CustomEvent): void {
|
||||||
|
const index = (ev.currentTarget as any).index;
|
||||||
|
fireEvent(this, "edit-entity", {
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
ha-button {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.entity {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.entity .handle {
|
||||||
|
cursor: move; /* fallback if grab cursor is unsupported */
|
||||||
|
cursor: grab;
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-inline-end: 8px;
|
||||||
|
padding-inline-start: initial;
|
||||||
|
direction: var(--direction);
|
||||||
|
}
|
||||||
|
.entity .handle > * {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity-content {
|
||||||
|
height: 60px;
|
||||||
|
font-size: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity-content div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-icon,
|
||||||
|
.edit-icon {
|
||||||
|
--mdc-icon-button-size: 36px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
li[divider] {
|
||||||
|
border-bottom-color: var(--divider-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
mwc-menu-surface {
|
||||||
|
--mdc-menu-min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-entity-picker {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-entities-editor": HuiEntitiesEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,352 @@
|
|||||||
|
import { mdiGestureTap, mdiListBox } from "@mdi/js";
|
||||||
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { cache } from "lit/directives/cache";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import {
|
||||||
|
any,
|
||||||
|
array,
|
||||||
|
assert,
|
||||||
|
assign,
|
||||||
|
literal,
|
||||||
|
object,
|
||||||
|
optional,
|
||||||
|
string,
|
||||||
|
union,
|
||||||
|
} from "superstruct";
|
||||||
|
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { LocalizeFunc } from "../../../../common/translations/localize";
|
||||||
|
import "../../../../components/ha-expansion-panel";
|
||||||
|
import "../../../../components/ha-form/ha-form";
|
||||||
|
import type {
|
||||||
|
HaFormSchema,
|
||||||
|
SchemaUnion,
|
||||||
|
} from "../../../../components/ha-form/types";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
|
import type { HomeAssistant } from "../../../../types";
|
||||||
|
import type {
|
||||||
|
HeadingCardConfig,
|
||||||
|
HeadingCardEntityConfig,
|
||||||
|
} from "../../cards/types";
|
||||||
|
import { UiAction } from "../../components/hui-action-editor";
|
||||||
|
import type { LovelaceCardEditor } from "../../types";
|
||||||
|
import "../hui-sub-form-editor";
|
||||||
|
import { processEditorEntities } from "../process-editor-entities";
|
||||||
|
import { actionConfigStruct } from "../structs/action-struct";
|
||||||
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
|
import { SubFormEditorData } from "../types";
|
||||||
|
import { configElementStyle } from "./config-elements-style";
|
||||||
|
import "./hui-entities-editor";
|
||||||
|
|
||||||
|
const actions: UiAction[] = ["navigate", "url", "perform-action", "none"];
|
||||||
|
|
||||||
|
const cardConfigStruct = assign(
|
||||||
|
baseLovelaceCardConfig,
|
||||||
|
object({
|
||||||
|
heading_style: optional(union([literal("title"), literal("subtitle")])),
|
||||||
|
heading: optional(string()),
|
||||||
|
icon: optional(string()),
|
||||||
|
tap_action: optional(actionConfigStruct),
|
||||||
|
entities: optional(array(any())),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const entityConfigStruct = object({
|
||||||
|
entity: string(),
|
||||||
|
content: optional(union([string(), array(string())])),
|
||||||
|
icon: optional(string()),
|
||||||
|
tap_action: optional(actionConfigStruct),
|
||||||
|
});
|
||||||
|
|
||||||
|
@customElement("hui-heading-card-editor")
|
||||||
|
export class HuiHeadingCardEditor
|
||||||
|
extends LitElement
|
||||||
|
implements LovelaceCardEditor
|
||||||
|
{
|
||||||
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
|
@state() private _config?: HeadingCardConfig;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private _entityFormEditorData?: SubFormEditorData<HeadingCardEntityConfig>;
|
||||||
|
|
||||||
|
public setConfig(config: HeadingCardConfig): void {
|
||||||
|
assert(config, cardConfigStruct);
|
||||||
|
this._config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public _assertEntityConfig(config: HeadingCardEntityConfig): void {
|
||||||
|
assert(config, entityConfigStruct);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _schema = memoizeOne(
|
||||||
|
(localize: LocalizeFunc) =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "heading_style",
|
||||||
|
selector: {
|
||||||
|
select: {
|
||||||
|
mode: "dropdown",
|
||||||
|
options: ["title", "subtitle"].map((value) => ({
|
||||||
|
label: localize(
|
||||||
|
`ui.panel.lovelace.editor.card.heading.heading_style_options.${value}`
|
||||||
|
),
|
||||||
|
value: value,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ name: "heading", selector: { text: {} } },
|
||||||
|
{
|
||||||
|
name: "icon",
|
||||||
|
selector: {
|
||||||
|
icon: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: "tap_action",
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "none",
|
||||||
|
actions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as const satisfies readonly HaFormSchema[]
|
||||||
|
);
|
||||||
|
|
||||||
|
private _entitySchema = memoizeOne(
|
||||||
|
() =>
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: "entity",
|
||||||
|
selector: { entity: {} },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "icon",
|
||||||
|
selector: { icon: {} },
|
||||||
|
context: { icon_entity: "entity" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "content",
|
||||||
|
selector: { ui_state_content: {} },
|
||||||
|
context: { filter_entity: "entity" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "interactions",
|
||||||
|
type: "expandable",
|
||||||
|
flatten: true,
|
||||||
|
iconPath: mdiGestureTap,
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: "tap_action",
|
||||||
|
selector: {
|
||||||
|
ui_action: {
|
||||||
|
default_action: "none",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as const satisfies readonly HaFormSchema[]
|
||||||
|
);
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this.hass || !this._config) {
|
||||||
|
return nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache(
|
||||||
|
this._entityFormEditorData ? this._renderEntityForm() : this._renderForm()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderEntityForm() {
|
||||||
|
const schema = this._entitySchema();
|
||||||
|
return html`
|
||||||
|
<hui-sub-form-editor
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.entities.form-label"
|
||||||
|
)}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.data=${this._entityFormEditorData!.data}
|
||||||
|
@go-back=${this._goBack}
|
||||||
|
@value-changed=${this._subFormChanged}
|
||||||
|
.schema=${schema}
|
||||||
|
.assertConfig=${this._assertEntityConfig}
|
||||||
|
.computeLabel=${this._computeEntityLabelCallback}
|
||||||
|
>
|
||||||
|
</hui-sub-form-editor>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entities = memoizeOne((entities: HeadingCardConfig["entities"]) =>
|
||||||
|
processEditorEntities(entities || [])
|
||||||
|
);
|
||||||
|
|
||||||
|
private _renderForm() {
|
||||||
|
const data = {
|
||||||
|
...this._config!,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!data.heading_style) {
|
||||||
|
data.heading_style = "title";
|
||||||
|
}
|
||||||
|
|
||||||
|
const schema = this._schema(this.hass!.localize);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-form
|
||||||
|
.hass=${this.hass}
|
||||||
|
.data=${data}
|
||||||
|
.schema=${schema}
|
||||||
|
.computeLabel=${this._computeLabelCallback}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-form>
|
||||||
|
<ha-expansion-panel outlined>
|
||||||
|
<h3 slot="header">
|
||||||
|
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.heading.entities"
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
<div class="content">
|
||||||
|
<hui-entities-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entities=${this._entities(this._config!.entities)}
|
||||||
|
@entities-changed=${this._entitiesChanged}
|
||||||
|
@edit-entity=${this._editEntity}
|
||||||
|
>
|
||||||
|
</hui-entities-editor>
|
||||||
|
</div>
|
||||||
|
</ha-expansion-panel>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entitiesChanged(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
...this._config,
|
||||||
|
entities: ev.detail.entities as HeadingCardEntityConfig[],
|
||||||
|
};
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = ev.detail.value as HeadingCardConfig;
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _subFormChanged(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (!this._config || !this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = ev.detail.value;
|
||||||
|
|
||||||
|
const newEntities = this._config!.entities
|
||||||
|
? [...this._config!.entities]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
newEntities.splice(this._entityFormEditorData!.index!, 1);
|
||||||
|
this._goBack();
|
||||||
|
} else {
|
||||||
|
newEntities[this._entityFormEditorData!.index!] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._config = { ...this._config!, entities: newEntities };
|
||||||
|
|
||||||
|
this._entityFormEditorData = {
|
||||||
|
...this._entityFormEditorData!,
|
||||||
|
data: value,
|
||||||
|
};
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config: this._config });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _editEntity(ev: HASSDomEvent<{ index: number }>): void {
|
||||||
|
const entities = this._entities(this._config!.entities);
|
||||||
|
this._entityFormEditorData = {
|
||||||
|
data: entities[ev.detail.index],
|
||||||
|
index: ev.detail.index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private _goBack(): void {
|
||||||
|
this._entityFormEditorData = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _computeEntityLabelCallback = (
|
||||||
|
schema: SchemaUnion<ReturnType<typeof this._entitySchema>>
|
||||||
|
) => {
|
||||||
|
switch (schema.name) {
|
||||||
|
case "content":
|
||||||
|
return this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.card.heading.entity_config.${schema.name}`
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private _computeLabelCallback = (
|
||||||
|
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||||
|
) => {
|
||||||
|
switch (schema.name) {
|
||||||
|
case "heading_style":
|
||||||
|
case "heading":
|
||||||
|
return this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.card.heading.${schema.name}`
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return this.hass!.localize(
|
||||||
|
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
configElementStyle,
|
||||||
|
css`
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
ha-form {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-heading-card-editor": HuiHeadingCardEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
|
import { mdiListBox } from "@mdi/js";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { cache } from "lit/directives/cache";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import {
|
import {
|
||||||
any,
|
any,
|
||||||
@ -12,11 +14,13 @@ import {
|
|||||||
string,
|
string,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
|
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type {
|
import type {
|
||||||
HaFormSchema,
|
HaFormSchema,
|
||||||
SchemaUnion,
|
SchemaUnion,
|
||||||
} from "../../../../components/ha-form/types";
|
} from "../../../../components/ha-form/types";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
LovelaceCardFeatureConfig,
|
LovelaceCardFeatureConfig,
|
||||||
@ -27,6 +31,7 @@ import type { LovelaceCardEditor } from "../../types";
|
|||||||
import "../hui-sub-element-editor";
|
import "../hui-sub-element-editor";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { EditSubElementEvent, SubElementEditorConfig } from "../types";
|
import { EditSubElementEvent, SubElementEditorConfig } from "../types";
|
||||||
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import "./hui-card-features-editor";
|
import "./hui-card-features-editor";
|
||||||
import type { FeatureType } from "./hui-card-features-editor";
|
import type { FeatureType } from "./hui-card-features-editor";
|
||||||
|
|
||||||
@ -93,16 +98,20 @@ export class HuiHumidifierCardEditor
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateObj = this._config.entity
|
return cache(
|
||||||
? this.hass.states[this._config.entity]
|
this._subElementEditorConfig
|
||||||
: undefined;
|
? this._renderFeatureForm()
|
||||||
|
: this._renderForm()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._subElementEditorConfig) {
|
private _renderFeatureForm() {
|
||||||
|
const entityId = this._config!.entity;
|
||||||
return html`
|
return html`
|
||||||
<hui-sub-element-editor
|
<hui-sub-element-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.config=${this._subElementEditorConfig}
|
.config=${this._subElementEditorConfig}
|
||||||
.context=${this._context(this._config.entity)}
|
.context=${this._context(entityId)}
|
||||||
@go-back=${this._goBack}
|
@go-back=${this._goBack}
|
||||||
@config-changed=${this.subElementChanged}
|
@config-changed=${this.subElementChanged}
|
||||||
>
|
>
|
||||||
@ -110,6 +119,10 @@ export class HuiHumidifierCardEditor
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderForm() {
|
||||||
|
const entityId = this._config!.entity;
|
||||||
|
const stateObj = entityId ? this.hass!.states[entityId] : undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-form
|
<ha-form
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -118,6 +131,14 @@ export class HuiHumidifierCardEditor
|
|||||||
.computeLabel=${this._computeLabelCallback}
|
.computeLabel=${this._computeLabelCallback}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
|
<ha-expansion-panel outlined>
|
||||||
|
<h3 slot="header">
|
||||||
|
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.generic.features"
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
<div class="content">
|
||||||
<hui-card-features-editor
|
<hui-card-features-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
@ -126,6 +147,8 @@ export class HuiHumidifierCardEditor
|
|||||||
@features-changed=${this._featuresChanged}
|
@features-changed=${this._featuresChanged}
|
||||||
@edit-detail-element=${this._editDetailElement}
|
@edit-detail-element=${this._editDetailElement}
|
||||||
></hui-card-features-editor>
|
></hui-card-features-editor>
|
||||||
|
</div>
|
||||||
|
</ha-expansion-panel>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,12 +225,15 @@ export class HuiHumidifierCardEditor
|
|||||||
};
|
};
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [
|
||||||
|
configElementStyle,
|
||||||
|
css`
|
||||||
ha-form {
|
ha-form {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
`;
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
|
|||||||
if (ev.detail && ev.detail.entities) {
|
if (ev.detail && ev.detail.entities) {
|
||||||
this._config = { ...this._config!, entities: ev.detail.entities };
|
this._config = { ...this._config!, entities: ev.detail.entities };
|
||||||
|
|
||||||
this._configEntities = processEditorEntities(this._config.entities);
|
this._configEntities = processEditorEntities(this._config.entities || []);
|
||||||
fireEvent(this, "config-changed", { config: this._config! });
|
fireEvent(this, "config-changed", { config: this._config! });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { mdiListBox } from "@mdi/js";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { cache } from "lit/directives/cache";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import {
|
import {
|
||||||
any,
|
any,
|
||||||
@ -12,11 +14,13 @@ import {
|
|||||||
string,
|
string,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
|
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type {
|
import type {
|
||||||
HaFormSchema,
|
HaFormSchema,
|
||||||
SchemaUnion,
|
SchemaUnion,
|
||||||
} from "../../../../components/ha-form/types";
|
} from "../../../../components/ha-form/types";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
LovelaceCardFeatureConfig,
|
LovelaceCardFeatureConfig,
|
||||||
@ -27,6 +31,7 @@ import type { LovelaceCardEditor } from "../../types";
|
|||||||
import "../hui-sub-element-editor";
|
import "../hui-sub-element-editor";
|
||||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
|
||||||
import { EditSubElementEvent, SubElementEditorConfig } from "../types";
|
import { EditSubElementEvent, SubElementEditorConfig } from "../types";
|
||||||
|
import { configElementStyle } from "./config-elements-style";
|
||||||
import "./hui-card-features-editor";
|
import "./hui-card-features-editor";
|
||||||
import type { FeatureType } from "./hui-card-features-editor";
|
import type { FeatureType } from "./hui-card-features-editor";
|
||||||
|
|
||||||
@ -91,16 +96,19 @@ export class HuiThermostatCardEditor
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateObj = this._config.entity
|
return cache(
|
||||||
? this.hass.states[this._config.entity]
|
this._subElementEditorConfig
|
||||||
: undefined;
|
? this._renderFeatureForm()
|
||||||
|
: this._renderForm()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._subElementEditorConfig) {
|
private _renderFeatureForm() {
|
||||||
return html`
|
return html`
|
||||||
<hui-sub-element-editor
|
<hui-sub-element-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.config=${this._subElementEditorConfig}
|
.config=${this._subElementEditorConfig}
|
||||||
.context=${this._context(this._config.entity)}
|
.context=${this._context(this._config!.entity)}
|
||||||
@go-back=${this._goBack}
|
@go-back=${this._goBack}
|
||||||
@config-changed=${this.subElementChanged}
|
@config-changed=${this.subElementChanged}
|
||||||
>
|
>
|
||||||
@ -108,6 +116,10 @@ export class HuiThermostatCardEditor
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderForm() {
|
||||||
|
const entityId = this._config!.entity;
|
||||||
|
const stateObj = entityId ? this.hass!.states[entityId] : undefined;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-form
|
<ha-form
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -116,6 +128,14 @@ export class HuiThermostatCardEditor
|
|||||||
.computeLabel=${this._computeLabelCallback}
|
.computeLabel=${this._computeLabelCallback}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
|
<ha-expansion-panel outlined>
|
||||||
|
<h3 slot="header">
|
||||||
|
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.generic.features"
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
<div class="content">
|
||||||
<hui-card-features-editor
|
<hui-card-features-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
@ -124,6 +144,8 @@ export class HuiThermostatCardEditor
|
|||||||
@features-changed=${this._featuresChanged}
|
@features-changed=${this._featuresChanged}
|
||||||
@edit-detail-element=${this._editDetailElement}
|
@edit-detail-element=${this._editDetailElement}
|
||||||
></hui-card-features-editor>
|
></hui-card-features-editor>
|
||||||
|
</div>
|
||||||
|
</ha-expansion-panel>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,12 +222,15 @@ export class HuiThermostatCardEditor
|
|||||||
};
|
};
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return css`
|
return [
|
||||||
|
configElementStyle,
|
||||||
|
css`
|
||||||
ha-form {
|
ha-form {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
`;
|
`,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { mdiGestureTap, mdiPalette } from "@mdi/js";
|
import { mdiGestureTap, mdiListBox, mdiPalette } from "@mdi/js";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { cache } from "lit/directives/cache";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import {
|
import {
|
||||||
any,
|
any,
|
||||||
@ -14,11 +15,13 @@ import {
|
|||||||
union,
|
union,
|
||||||
} from "superstruct";
|
} from "superstruct";
|
||||||
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
|
import { HASSDomEvent, fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-expansion-panel";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
import type {
|
import type {
|
||||||
HaFormSchema,
|
HaFormSchema,
|
||||||
SchemaUnion,
|
SchemaUnion,
|
||||||
} from "../../../../components/ha-form/types";
|
} from "../../../../components/ha-form/types";
|
||||||
|
import "../../../../components/ha-svg-icon";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
LovelaceCardFeatureConfig,
|
LovelaceCardFeatureConfig,
|
||||||
@ -168,21 +171,19 @@ export class HuiTileCardEditor
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateObj = this._config.entity
|
return cache(
|
||||||
? this.hass.states[this._config.entity]
|
this._subElementEditorConfig
|
||||||
: undefined;
|
? this._renderFeatureForm()
|
||||||
|
: this._renderForm()
|
||||||
const schema = this._schema(
|
|
||||||
this._config.entity,
|
|
||||||
this._config.hide_state ?? false
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._subElementEditorConfig) {
|
private _renderFeatureForm() {
|
||||||
return html`
|
return html`
|
||||||
<hui-sub-element-editor
|
<hui-sub-element-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.config=${this._subElementEditorConfig}
|
.config=${this._subElementEditorConfig}
|
||||||
.context=${this._context(this._config.entity)}
|
.context=${this._context(this._config!.entity)}
|
||||||
@go-back=${this._goBack}
|
@go-back=${this._goBack}
|
||||||
@config-changed=${this.subElementChanged}
|
@config-changed=${this.subElementChanged}
|
||||||
>
|
>
|
||||||
@ -190,6 +191,12 @@ export class HuiTileCardEditor
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _renderForm() {
|
||||||
|
const entityId = this._config!.entity;
|
||||||
|
const stateObj = entityId ? this.hass!.states[entityId] : undefined;
|
||||||
|
|
||||||
|
const schema = this._schema(entityId, this._config!.hide_state ?? false);
|
||||||
|
|
||||||
const data = this._config;
|
const data = this._config;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@ -200,6 +207,14 @@ export class HuiTileCardEditor
|
|||||||
.computeLabel=${this._computeLabelCallback}
|
.computeLabel=${this._computeLabelCallback}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
></ha-form>
|
></ha-form>
|
||||||
|
<ha-expansion-panel outlined>
|
||||||
|
<h3 slot="header">
|
||||||
|
<ha-svg-icon .path=${mdiListBox}></ha-svg-icon>
|
||||||
|
${this.hass!.localize(
|
||||||
|
"ui.panel.lovelace.editor.card.generic.features"
|
||||||
|
)}
|
||||||
|
</h3>
|
||||||
|
<div class="content">
|
||||||
<hui-card-features-editor
|
<hui-card-features-editor
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
@ -207,6 +222,8 @@ export class HuiTileCardEditor
|
|||||||
@features-changed=${this._featuresChanged}
|
@features-changed=${this._featuresChanged}
|
||||||
@edit-detail-element=${this._editDetailElement}
|
@edit-detail-element=${this._editDetailElement}
|
||||||
></hui-card-features-editor>
|
></hui-card-features-editor>
|
||||||
|
</div>
|
||||||
|
</ha-expansion-panel>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
190
src/panels/lovelace/editor/hui-sub-form-editor.ts
Normal file
190
src/panels/lovelace/editor/hui-sub-form-editor.ts
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import { mdiCodeBraces, mdiListBoxOutline } from "@mdi/js";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResultGroup,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
nothing,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit";
|
||||||
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import "../../../components/ha-form/ha-form";
|
||||||
|
import "../../../components/ha-icon-button";
|
||||||
|
import "../../../components/ha-icon-button-prev";
|
||||||
|
import "../../../components/ha-yaml-editor";
|
||||||
|
import "../../../components/ha-alert";
|
||||||
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import type { LovelaceConfigForm } from "../types";
|
||||||
|
import type { EditSubFormEvent } from "./types";
|
||||||
|
import { handleStructError } from "../../../common/structs/handle-errors";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"go-back": undefined;
|
||||||
|
"edit-sub-form": EditSubFormEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("hui-sub-form-editor")
|
||||||
|
export class HuiSubFormEditor<T = any> extends LitElement {
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public label?: string;
|
||||||
|
|
||||||
|
@property({ attribute: false }) public data!: T;
|
||||||
|
|
||||||
|
public schema!: LovelaceConfigForm["schema"];
|
||||||
|
|
||||||
|
public assertConfig?: (config: T) => void;
|
||||||
|
|
||||||
|
public computeLabel?: LovelaceConfigForm["computeLabel"];
|
||||||
|
|
||||||
|
public computeHelper?: LovelaceConfigForm["computeHelper"];
|
||||||
|
|
||||||
|
@state() public _yamlMode = false;
|
||||||
|
|
||||||
|
@state() private _errors?: string[];
|
||||||
|
|
||||||
|
@state() private _warnings?: string[];
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const uiAvailable = !this.hasWarning && !this.hasError;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="header">
|
||||||
|
<div class="back-title">
|
||||||
|
<ha-icon-button-prev
|
||||||
|
.label=${this.hass!.localize("ui.common.back")}
|
||||||
|
@click=${this._goBack}
|
||||||
|
></ha-icon-button-prev>
|
||||||
|
<span slot="title">${this.label}</span>
|
||||||
|
</div>
|
||||||
|
<ha-icon-button
|
||||||
|
class="gui-mode-button"
|
||||||
|
@click=${this._toggleMode}
|
||||||
|
.disabled=${!uiAvailable}
|
||||||
|
.label=${this.hass!.localize(
|
||||||
|
this._yamlMode
|
||||||
|
? "ui.panel.lovelace.editor.edit_card.show_visual_editor"
|
||||||
|
: "ui.panel.lovelace.editor.edit_card.show_code_editor"
|
||||||
|
)}
|
||||||
|
.path=${this._yamlMode ? mdiListBoxOutline : mdiCodeBraces}
|
||||||
|
></ha-icon-button>
|
||||||
|
</div>
|
||||||
|
${this._yamlMode
|
||||||
|
? html`
|
||||||
|
<ha-yaml-editor
|
||||||
|
.hass=${this.hass}
|
||||||
|
.defaultValue=${this.data}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-form
|
||||||
|
.hass=${this.hass}
|
||||||
|
.schema=${this.schema}
|
||||||
|
.computeLabel=${this.computeLabel}
|
||||||
|
.computeHelper=${this.computeHelper}
|
||||||
|
.data=${this.data}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
>
|
||||||
|
</ha-form>
|
||||||
|
`}
|
||||||
|
${this.hasError
|
||||||
|
? html`
|
||||||
|
<ha-alert alert-type="error">
|
||||||
|
${this.hass.localize("ui.errors.config.error_detected")}:
|
||||||
|
<br />
|
||||||
|
<ul>
|
||||||
|
${this._errors!.map((error) => html`<li>${error}</li>`)}
|
||||||
|
</ul>
|
||||||
|
</ha-alert>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${this.hasWarning
|
||||||
|
? html`
|
||||||
|
<ha-alert
|
||||||
|
alert-type="warning"
|
||||||
|
.title="${this.hass.localize(
|
||||||
|
"ui.errors.config.editor_not_supported"
|
||||||
|
)}:"
|
||||||
|
>
|
||||||
|
${this._warnings!.length > 0 && this._warnings![0] !== undefined
|
||||||
|
? html`
|
||||||
|
<ul>
|
||||||
|
${this._warnings!.map(
|
||||||
|
(warning) => html`<li>${warning}</li>`
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
${this.hass.localize("ui.errors.config.edit_in_yaml_supported")}
|
||||||
|
</ha-alert>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected willUpdate(changedProperties: PropertyValues<this>) {
|
||||||
|
if (changedProperties.has("data")) {
|
||||||
|
if (this.assertConfig) {
|
||||||
|
try {
|
||||||
|
this.assertConfig(this.data);
|
||||||
|
this._warnings = undefined;
|
||||||
|
this._errors = undefined;
|
||||||
|
} catch (err: any) {
|
||||||
|
const msgs = handleStructError(this.hass, err);
|
||||||
|
this._warnings = msgs.warnings ?? [err.message];
|
||||||
|
this._errors = msgs.errors || undefined;
|
||||||
|
this._yamlMode = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get hasWarning(): boolean {
|
||||||
|
return this._warnings !== undefined && this._warnings.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get hasError(): boolean {
|
||||||
|
return this._errors !== undefined && this._errors.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _goBack(): void {
|
||||||
|
fireEvent(this, "go-back");
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleMode(): void {
|
||||||
|
this._yamlMode = !this._yamlMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const value = (ev.detail.value ?? (ev.target as any).value ?? {}) as T;
|
||||||
|
fireEvent(this, "value-changed", { value });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return css`
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.back-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"hui-sub-form-editor": HuiSubFormEditor;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user