Compare commits

...

50 Commits

Author SHA1 Message Date
Paul Bottein
76380c189b Add form/code editor switch 2024-09-09 19:07:55 +02:00
Paul Bottein
c8b7f373c3 Only use toggle mode in card editor 2024-09-09 18:32:16 +02:00
Paul Bottein
4f2652abd2 Improve error messages 2024-09-09 17:46:41 +02:00
Paul Bottein
6d8b7f6995 Use ha alert inside card editor 2024-09-09 17:46:41 +02:00
Paul Bottein
bbf8a8e3e7 Add global yaml editor 2024-09-09 17:46:41 +02:00
Paul Bottein
14308c9057 Use overflow menu for global toggle 2024-09-09 17:46:41 +02:00
Paul Bottein
b87f44ff74 WIP: continue migration 2024-09-09 17:46:41 +02:00
Paul Bottein
36540aa8fb Create card editor 2024-09-09 17:46:41 +02:00
Paul Bottein
bde2fd8202 Bumped version to 20240909.1 2024-09-09 17:17:04 +02:00
Paul Bottein
e5327c0903 Update patch for sortablejs 1.15.3 (#21934)
Update sortablejs patch
2024-09-09 15:15:53 +00:00
Yosi Levy
1a0ca1b78f RTL fixes sep 24 (#21893)
* Fix logs drop down

* Fix history arrow

* Icon direction fix
2024-09-09 17:13:23 +02:00
Paul Bottein
ed141b1d12 Bumped version to 20240909.0 2024-09-09 16:35:16 +02:00
Paul Bottein
5a7a71c551 Fix section view crashing on old iPads (#21932) 2024-09-09 10:30:27 +00:00
karwosts
f09e0d187b Restore localizeValue to ha-form (fix selector translations) (#21923) 2024-09-09 11:20:08 +02:00
karwosts
7f6325fa5e Fix sections item translation for config flow (#21924) 2024-09-08 15:24:59 +00:00
jonnynch
de292a8143 Fix WebRTC for Firefox by ignoring empty ice candidates (#21908)
* handle firefox empty string ice candidate

* use optional chaining as per comment
2024-09-08 17:23:47 +02:00
renovate[bot]
84b2005844 Update dependency marked to v14.1.1 (#21917)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-07 08:27:00 +02:00
renovate[bot]
0d93432a2c Update dependency eslint-import-resolver-webpack to v0.13.9 (#21909)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-06 22:46:05 +00:00
Bram Kragten
8bc9927ee2 Zwave JS display allowed range of config values (#21892)
* Zwave: Display allowed range of config values, catch wrong values

* allow min and max

* Update zwave_js-node-config.ts
2024-09-07 00:38:32 +02:00
Bram Kragten
484bed4dab Fix initial form data for action/condition/trigger selectors (#21899) 2024-09-07 00:34:59 +02:00
renovate[bot]
3d7e243707 Update dependency sortablejs to v1.15.3 (#21885)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-07 00:31:08 +02:00
renovate[bot]
f8a432c89e Update dependency eslint-plugin-import to v2.30.0 (#21910)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-07 00:29:53 +02:00
renovate[bot]
d484b2f63d Update dependency webpack-dev-server to v5.1.0 (#21914)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-07 00:28:06 +02:00
Paul Bottein
30d9186031 Bumped version to 20240906.0 2024-09-06 13:46:51 +02:00
Paul Bottein
cd74367acc Use primary config entry for device (#21903)
* Use primary config entry for device

* Fix types
2024-09-06 13:43:29 +02:00
Bram Kragten
618cd9d9e5 Remove device subscription from zwave node config (#21891)
remove device subscription from zwave node config
2024-09-05 13:40:10 +02:00
Paul Bottein
0ff2f1bf75 Hide top label for number selector using box mode (#21888) 2024-09-05 10:22:39 +02:00
renovate[bot]
d28f1f07e7 Update dependency lint-staged to v15.2.10 (#21881)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-05 10:05:06 +02:00
Bram Kragten
6aa5bc2d8b Bumped version to 20240904.0 2024-09-04 10:50:16 +02:00
Bram Kragten
76fc0c7ab1 Change update logic in ha-data-table (#21874)
* Change update logic in ha-data-table

* use time of last request
2024-09-04 10:49:32 +02:00
Paul Bottein
7aa7019386 Move badge styling into ha-badge component to reuse it (#21864)
* Move badge styling into ha-badge component to reuse it

* Fix error badge

* Update src/components/ha-badge.ts

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

---------

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2024-09-04 10:48:55 +02:00
Joakim Sørensen
b69f0964c9 Fix compression of hassio builds (#21869) 2024-09-04 10:01:58 +02:00
renovate[bot]
2f9b6d000b Update dependency @codemirror/commands to v6.6.1 (#21863)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-04 07:48:35 +02:00
Paul Bottein
94f186c436 Bumped version to 20240903.1 2024-09-03 18:51:49 +02:00
Bram Kragten
449f858ac8 Merge branch 'master' into dev 2024-09-03 18:47:42 +02:00
Paul Bottein
91a2f2cf24 Bumped version to 20240903.0 2024-09-03 18:37:42 +02:00
Paul Bottein
2c975d4f41 Add advanced yaml only row_span option for sections (#21833) 2024-09-03 18:20:55 +02:00
renovate[bot]
ab534933fc Update dependency @babel/runtime to v7.25.6 (#21847)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 11:37:28 -04:00
renovate[bot]
e353aaa339 Update vaadinWebComponents monorepo to v24.4.7 (#21854)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-03 11:35:44 -04:00
Paul Bottein
020904f8f6 Hide section title when the section is hidden (#21862) 2024-09-03 10:41:15 +02:00
Wendelin
fa8b3f006d Fix autofill for ha-selector-text (#21861) 2024-09-03 09:35:17 +02:00
Paul Bottein
a09f44dcd2 Bumped version to 20240902.0 2024-09-02 17:29:27 +02:00
Simon Lamon
c709059c00 Perform action on every entity (#21845) 2024-09-02 17:23:46 +02:00
Joakim Sørensen
5613df1d01 Fix rendering of alerts in markdown when not breaking (#21856) 2024-09-02 17:21:58 +02:00
Paul Bottein
d8013a4db9 Move edit mode actions next to section block (#21840) 2024-09-02 13:21:24 +02:00
Paul Bottein
216dbc4d41 Fix section not displayed when empty and string config (#21852) 2024-09-02 13:21:13 +02:00
Simon Lamon
c40751dadd Add padding to no info badge (#21844)
* Add padding to no info badge

* Update src/panels/lovelace/badges/hui-entity-badge.ts

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>

---------

Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
2024-09-02 12:35:28 +02:00
dependabot[bot]
f58d3ad670 Bump actions/upload-artifact from 4.3.6 to 4.4.0 (#21850)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.6 to 4.4.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.3.6...v4.4.0)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-02 12:00:26 +02:00
renovate[bot]
682f5345cc Update dependency marked to v14.1.0 (#21829)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-29 22:35:42 -04:00
renovate[bot]
a69771c1f8 Update dependency @bundle-stats/plugin-webpack-filter to v4.15.0 (#21837)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-29 22:33:57 -04:00
37 changed files with 1118 additions and 870 deletions

View File

@@ -1,16 +1,7 @@
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
+++ 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 */{
}
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, !!target) !== false) {
@@ -33,7 +24,7 @@ index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b
}
parentEl = el; // actualization
@@ -1802,7 +1807,13 @@ Sortable.prototype = /** @lends Sortable.prototype */{
@@ -1802,7 +1807,12 @@ Sortable.prototype = /** @lends Sortable.prototype */{
targetRect = getRect(target);
if (_onMove(rootEl, el, dragEl, dragRect, target, targetRect, evt, false) !== false) {
capture();
@@ -44,11 +35,10 @@ index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b
+ catch(err) {
+ return completed(false);
+ }
+
parentEl = el; // actualization
changed();
@@ -1849,12 +1860,17 @@ Sortable.prototype = /** @lends Sortable.prototype */{
@@ -1849,10 +1859,15 @@ Sortable.prototype = /** @lends Sortable.prototype */{
_silent = true;
setTimeout(_unsilent, 30);
capture();
@@ -56,8 +46,6 @@ index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b
- el.appendChild(dragEl);
- } else {
- target.parentNode.insertBefore(dragEl, after ? nextSibling : target);
- }
+ try {
+ if (after && !nextSibling) {
+ el.appendChild(dragEl);
@@ -67,7 +55,6 @@ index 93ba17509e2e8583ab241fea6845fbe714c584a2..de0651ddb5dced30d36f7d764da0dd0b
+ }
+ catch(err) {
+ return completed(false);
+ }
}
// Undo chrome's scroll adjustment (has no effect on other browsers)
if (scrolledPastTop) {
scrollBy(scrolledPastTop, 0, scrollBefore - scrolledPastTop.scrollTop);

View File

@@ -15,23 +15,29 @@ const brotliOptions = {
};
const zopfliOptions = { threshold: 150 };
const compressDistBrotli = (rootDir, modernDir) =>
const compressDistBrotli = (rootDir, modernDir, compressServiceWorker = true) =>
gulp
.src([`${modernDir}/**/${filesGlob}`, `${rootDir}/sw-modern.js`], {
base: rootDir,
})
.src(
[
`${modernDir}/**/${filesGlob}`,
compressServiceWorker ? `${rootDir}/sw-modern.js` : undefined,
].filter(Boolean),
{
base: rootDir,
}
)
.pipe(brotli(brotliOptions))
.pipe(gulp.dest(rootDir));
const compressDistZopfli = (rootDir, modernDir) =>
const compressDistZopfli = (rootDir, modernDir, compressModern = false) =>
gulp
.src(
[
`${rootDir}/**/${filesGlob}`,
`!${modernDir}/**/${filesGlob}`,
compressModern ? undefined : `!${modernDir}/**/${filesGlob}`,
`!${rootDir}/{sw-modern,service_worker}.js`,
`${rootDir}/{authorize,onboarding}.html`,
],
].filter(Boolean),
{ base: rootDir }
)
.pipe(zopfli(zopfliOptions))
@@ -40,12 +46,20 @@ const compressDistZopfli = (rootDir, modernDir) =>
const compressAppBrotli = () =>
compressDistBrotli(paths.app_output_root, paths.app_output_latest);
const compressHassioBrotli = () =>
compressDistBrotli(paths.hassio_output_root, paths.hassio_output_latest);
compressDistBrotli(
paths.hassio_output_root,
paths.hassio_output_latest,
false
);
const compressAppZopfli = () =>
compressDistZopfli(paths.app_output_root, paths.app_output_latest);
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(

View File

@@ -64,6 +64,7 @@ const DEVICES: DeviceRegistryEntry[] = [
labels: [],
created_at: 0,
modified_at: 0,
primary_config_entry: null,
},
{
area_id: "backyard",
@@ -86,6 +87,7 @@ const DEVICES: DeviceRegistryEntry[] = [
labels: [],
created_at: 0,
modified_at: 0,
primary_config_entry: null,
},
{
area_id: null,
@@ -108,6 +110,7 @@ const DEVICES: DeviceRegistryEntry[] = [
labels: [],
created_at: 0,
modified_at: 0,
primary_config_entry: null,
},
];

View File

@@ -64,6 +64,7 @@ const DEVICES: DeviceRegistryEntry[] = [
labels: [],
created_at: 0,
modified_at: 0,
primary_config_entry: null,
},
{
area_id: "backyard",
@@ -86,6 +87,7 @@ const DEVICES: DeviceRegistryEntry[] = [
labels: [],
created_at: 0,
modified_at: 0,
primary_config_entry: null,
},
{
area_id: null,
@@ -108,6 +110,7 @@ const DEVICES: DeviceRegistryEntry[] = [
labels: [],
created_at: 0,
modified_at: 0,
primary_config_entry: null,
},
];

View File

@@ -232,6 +232,7 @@ const createDeviceRegistryEntries = (
labels: [],
created_at: 0,
modified_at: 0,
primary_config_entry: null,
},
];

View File

@@ -25,10 +25,10 @@
"license": "Apache-2.0",
"type": "module",
"dependencies": {
"@babel/runtime": "7.25.4",
"@babel/runtime": "7.25.6",
"@braintree/sanitize-url": "7.1.0",
"@codemirror/autocomplete": "6.18.0",
"@codemirror/commands": "6.6.0",
"@codemirror/commands": "6.6.1",
"@codemirror/language": "6.10.2",
"@codemirror/legacy-modes": "6.4.1",
"@codemirror/search": "6.5.6",
@@ -88,8 +88,8 @@
"@polymer/paper-tabs": "3.1.0",
"@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.4.6",
"@vaadin/vaadin-themable-mixin": "24.4.6",
"@vaadin/combo-box": "24.4.7",
"@vaadin/vaadin-themable-mixin": "24.4.7",
"@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@@ -118,7 +118,7 @@
"leaflet-draw": "1.0.4",
"lit": "2.8.0",
"luxon": "3.5.0",
"marked": "14.1.0",
"marked": "14.1.1",
"memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2",
@@ -127,7 +127,7 @@
"qrcode": "1.5.4",
"roboto-fontface": "0.10.0",
"rrule": "2.8.1",
"sortablejs": "1.15.2",
"sortablejs": "1.15.3",
"stacktrace-js": "2.0.2",
"superstruct": "2.0.2",
"tinykeys": "3.0.0",
@@ -198,8 +198,8 @@
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb-typescript": "18.0.0",
"eslint-config-prettier": "9.1.0",
"eslint-import-resolver-webpack": "0.13.8",
"eslint-plugin-import": "2.29.1",
"eslint-import-resolver-webpack": "0.13.9",
"eslint-plugin-import": "2.30.0",
"eslint-plugin-lit": "1.14.0",
"eslint-plugin-lit-a11y": "4.1.4",
"eslint-plugin-unused-imports": "4.1.3",
@@ -216,7 +216,7 @@
"husky": "9.1.5",
"instant-mocha": "1.5.2",
"jszip": "3.10.1",
"lint-staged": "15.2.9",
"lint-staged": "15.2.10",
"lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2",
"lodash.template": "4.5.0",
@@ -241,7 +241,7 @@
"typescript": "5.5.4",
"webpack": "5.94.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.0.4",
"webpack-dev-server": "5.1.0",
"webpack-manifest-plugin": "5.0.0",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "6.0.1",
@@ -255,7 +255,7 @@
"clean-css": "5.3.3",
"@lit/reactive-element": "1.6.3",
"@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"
},
"packageManager": "yarn@4.4.1"

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20240902.0"
version = "20240909.1"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

@@ -25,7 +25,6 @@ import { fireEvent } from "../../common/dom/fire_event";
import { stringCompare } from "../../common/string/compare";
import { debounce } from "../../common/util/debounce";
import { groupBy } from "../../common/util/group-by";
import { nextRender } from "../../common/util/render-status";
import { haStyleScrollbar } from "../../resources/styles";
import { loadVirtualizer } from "../../resources/virtualizer";
import { HomeAssistant } from "../../types";
@@ -35,6 +34,7 @@ import "../ha-svg-icon";
import "../search-input";
import { filterData, sortData } from "./sort-filter";
import { LocalizeFunc } from "../../common/translations/localize";
import { nextRender } from "../../common/util/render-status";
export interface RowClickedEvent {
id: string;
@@ -169,8 +169,6 @@ export class HaDataTable extends LitElement {
@query("slot[name='header']") private _header!: HTMLSlotElement;
@state() private _items: DataTableRowData[] = [];
@state() private _collapsedGroups: string[] = [];
private _checkableRowsCount?: number;
@@ -179,7 +177,9 @@ export class HaDataTable extends LitElement {
private _sortColumns: SortableColumnContainer = {};
private curRequest = 0;
private _curRequest = 0;
private _lastUpdate = 0;
// @ts-ignore
@restoreScroll(".scroller") private _savedScrollPos?: number;
@@ -206,9 +206,9 @@ export class HaDataTable extends LitElement {
public connectedCallback() {
super.connectedCallback();
if (this._items.length) {
if (this._filteredData.length) {
// 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("_filter") ||
properties.has("sortColumn") ||
properties.has("sortDirection") ||
properties.has("groupColumn") ||
properties.has("groupOrder") ||
properties.has("_collapsedGroups")
properties.has("sortDirection")
) {
this._sortFilterData();
}
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
class="mdc-data-table__content scroller ha-scrollbar"
@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}
.renderItem=${renderRow}
></lit-virtualizer>
@@ -602,8 +607,13 @@ export class HaDataTable extends LitElement {
private async _sortFilterData() {
const startTime = new Date().getTime();
this.curRequest++;
const curRequest = this.curRequest;
const timeBetweenUpdate = startTime - this._lastUpdate;
const timeBetweenRequest = startTime - this._curRequest;
this._curRequest = startTime;
const forceUpdate =
!this._lastUpdate ||
(timeBetweenUpdate > 500 && timeBetweenRequest < 500);
let filteredData = this.data;
if (this._filter) {
@@ -614,6 +624,10 @@ export class HaDataTable extends LitElement {
);
}
if (!forceUpdate && this._curRequest !== startTime) {
return;
}
const prom = this.sortColumn
? sortData(
filteredData,
@@ -634,91 +648,103 @@ export class HaDataTable extends LitElement {
setTimeout(resolve, 100 - elapsed);
});
}
if (this.curRequest !== curRequest) {
if (!forceUpdate && this._curRequest !== startTime) {
return;
}
const localize = this.localizeFunc || this.hass.localize;
if (this.appendRow || this.hasFab || this.groupColumn) {
let items = [...data];
if (this.groupColumn) {
const grouped = groupBy(items, (item) => item[this.groupColumn!]);
if (grouped.undefined) {
// make sure ungrouped items are at the bottom
grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
delete grouped.undefined;
}
const sorted: {
[key: string]: DataTableRowData[];
} = Object.keys(grouped)
.sort((a, b) => {
const orderA = this.groupOrder?.indexOf(a) ?? -1;
const orderB = this.groupOrder?.indexOf(b) ?? -1;
if (orderA !== orderB) {
if (orderA === -1) {
return 1;
}
if (orderB === -1) {
return -1;
}
return orderA - orderB;
}
return stringCompare(
["", "-", "—"].includes(a) ? "zzz" : a,
["", "-", "—"].includes(b) ? "zzz" : b,
this.hass.locale.language
);
})
.reduce((obj, key) => {
obj[key] = grouped[key];
return obj;
}, {});
const groupedItems: DataTableRowData[] = [];
Object.entries(sorted).forEach(([groupName, rows]) => {
groupedItems.push({
append: true,
content: html`<div
class="mdc-data-table__cell group-header"
role="cell"
.group=${groupName}
@click=${this._collapseGroup}
>
<ha-icon-button
.path=${mdiChevronUp}
class=${this._collapsedGroups.includes(groupName)
? "collapsed"
: ""}
>
</ha-icon-button>
${groupName === UNDEFINED_GROUP_KEY
? localize("ui.components.data-table.ungrouped")
: groupName || ""}
</div>`,
});
if (!this._collapsedGroups.includes(groupName)) {
groupedItems.push(...rows);
}
});
items = groupedItems;
}
if (this.appendRow) {
items.push({ append: true, content: this.appendRow });
}
if (this.hasFab) {
items.push({ empty: true });
}
this._items = items;
} else {
this._items = data;
}
this._lastUpdate = startTime;
this._filteredData = data;
}
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];
if (groupColumn) {
const grouped = groupBy(items, (item) => item[groupColumn]);
if (grouped.undefined) {
// make sure ungrouped items are at the bottom
grouped[UNDEFINED_GROUP_KEY] = grouped.undefined;
delete grouped.undefined;
}
const sorted: {
[key: string]: DataTableRowData[];
} = Object.keys(grouped)
.sort((a, b) => {
const orderA = groupOrder?.indexOf(a) ?? -1;
const orderB = groupOrder?.indexOf(b) ?? -1;
if (orderA !== orderB) {
if (orderA === -1) {
return 1;
}
if (orderB === -1) {
return -1;
}
return orderA - orderB;
}
return stringCompare(
["", "-", "—"].includes(a) ? "zzz" : a,
["", "-", "—"].includes(b) ? "zzz" : b,
this.hass.locale.language
);
})
.reduce((obj, key) => {
obj[key] = grouped[key];
return obj;
}, {});
const groupedItems: DataTableRowData[] = [];
Object.entries(sorted).forEach(([groupName, rows]) => {
groupedItems.push({
append: true,
content: html`<div
class="mdc-data-table__cell group-header"
role="cell"
.group=${groupName}
@click=${this._collapseGroup}
>
<ha-icon-button
.path=${mdiChevronUp}
class=${collapsedGroups.includes(groupName)
? "collapsed"
: ""}
>
</ha-icon-button>
${groupName === UNDEFINED_GROUP_KEY
? localize("ui.components.data-table.ungrouped")
: groupName || ""}
</div>`,
});
if (!collapsedGroups.includes(groupName)) {
groupedItems.push(...rows);
}
});
items = groupedItems;
}
if (appendRow) {
items.push({ append: true, content: appendRow });
}
if (hasFab) {
items.push({ empty: true });
}
return items;
}
return data;
}
);
private _memFilterData = memoizeOne(
(
data: DataTableRowData[],
@@ -802,8 +828,8 @@ export class HaDataTable extends LitElement {
private _checkedRowsChanged() {
// force scroller to update, change it's items
if (this._items.length) {
this._items = [...this._items];
if (this._filteredData.length) {
this._filteredData = [...this._filteredData];
}
fireEvent(this, "selection-changed", {
value: this._checkedRows,

155
src/components/ha-badge.ts Normal file
View 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;
}
}

View File

@@ -1,4 +1,5 @@
import { Button } from "@material/mwc-button";
import { Corner } from "@material/web/menu/menu";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { FOCUS_TARGET } from "../dialogs/make-dialog-manager";
@@ -14,8 +15,20 @@ export class HaButtonMenuNew extends LitElement {
@property() public positioning?: "fixed" | "absolute" | "popover";
@property({ type: Boolean, attribute: "has-overflow" }) public hasOverflow =
false;
@property({ type: Boolean, attribute: "no-horizontal-flip" })
public noHorizontalFlip = false;
@property({ type: Boolean, attribute: "no-vertical-flip" })
public noVerticalFlip = false;
@property({ attribute: "anchor-corner" })
public anchorCorner: Corner = Corner.END_START;
@property({ attribute: "menu-corner" })
public menuCorner: Corner = Corner.START_START;
@property({ type: Boolean, attribute: "has-overflow" })
public hasOverflow = false;
@query("ha-menu", true) private _menu!: HaMenu;
@@ -39,6 +52,10 @@ export class HaButtonMenuNew extends LitElement {
<ha-menu
.positioning=${this.positioning}
.hasOverflow=${this.hasOverflow}
.anchorCorner=${this.anchorCorner}
.menuCorner=${this.menuCorner}
.noVerticalFlip=${this.noVerticalFlip}
.noHorizontalFlip=${this.noHorizontalFlip}
>
<slot></slot>
</ha-menu>

View File

@@ -95,10 +95,10 @@ export const computeInitialHaFormData = (
} else if (
"action" in selector ||
"trigger" in selector ||
"condition" in selector ||
"media" in selector ||
"target" in selector
"condition" in selector
) {
data[field.name] = [];
} else if ("media" in selector || "target" in selector) {
data[field.name] = {};
} else {
throw new Error(

View File

@@ -73,6 +73,10 @@ export class HaForm extends LitElement implements HaFormElement {
schema: any
) => string | undefined;
@property({ attribute: false }) public localizeValue?: (
key: string
) => string;
protected getFormProperties(): Record<string, any> {
return {};
}
@@ -145,6 +149,7 @@ export class HaForm extends LitElement implements HaFormElement {
.disabled=${item.disabled || this.disabled || false}
.placeholder=${item.required ? "" : item.default}
.helper=${this._computeHelper(item)}
.localizeValue=${this.localizeValue}
.required=${item.required || false}
.context=${this._generateContext(item)}
></ha-selector>`

View File

@@ -0,0 +1,22 @@
import { MdOutlinedSegmentedButtonSet } from "@material/web/labs/segmentedbuttonset/outlined-segmented-button-set";
import { css } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-outlined-segmented-button-set")
export class HaOutlinedSegmentedButtonSet extends MdOutlinedSegmentedButtonSet {
static override styles = [
...super.styles,
css`
:host {
--ha-icon-display: block;
--md-outlined-segmented-button-container-height: 32px;
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-outlined-segmented-button-set": HaOutlinedSegmentedButtonSet;
}
}

View File

@@ -0,0 +1,34 @@
import { MdOutlinedSegmentedButton } from "@material/web/labs/segmentedbutton/outlined-segmented-button";
import { css } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-outlined-segmented-button")
export class HaOutlinedSegmentedButton extends MdOutlinedSegmentedButton {
static override styles = [
...super.styles,
css`
:host {
--ha-icon-display: block;
--md-outlined-segmented-button-selected-container-color: var(
--light-primary-color
);
--md-outlined-segmented-button-container-height: 32px;
--md-outlined-segmented-button-disabled-label-text-color: var(
--disabled-text-color
);
--md-outlined-segmented-button-disabled-icon-color: var(
--disabled-text-color
);
--md-outlined-segmented-button-disabled-outline-color: var(
--disabled-text-color
);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
"ha-outlined-segmented-button": HaOutlinedSegmentedButton;
}
}

View File

@@ -67,7 +67,9 @@ export class HaNumberSelector extends LitElement {
}
return html`
${this.label ? html`${this.label}${this.required ? "*" : ""}` : nothing}
${this.label && !isBox
? html`${this.label}${this.required ? "*" : ""}`
: nothing}
<div class="input">
${!isBox
? html`

View File

@@ -82,6 +82,7 @@ export class HaTextSelector extends LitElement {
.disabled=${this.disabled}
.type=${this._unmaskedPassword ? "text" : this.selector.text?.type}
@input=${this._handleChange}
@change=${this._handleChange}
.label=${this.label || ""}
.prefix=${this.selector.text?.prefix}
.suffix=${this.selector.text?.type === "password"

View File

@@ -109,7 +109,7 @@ class HaWebRtcPlayer extends LitElement {
let candidates = ""; // Build an Offer SDP string with ice candidates
const iceResolver = new Promise<void>((resolve) => {
peerConnection.addEventListener("icecandidate", async (event) => {
if (!event.candidate) {
if (!event.candidate?.candidate) {
resolve(); // Gathering complete
return;
}

View File

@@ -1,6 +1,6 @@
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { HomeAssistant } from "../types";
import type { IntegrationManifest, IntegrationType } from "./integration";
import type { IntegrationType } from "./integration";
export interface ConfigEntry {
entry_id: string;
@@ -149,20 +149,19 @@ export const enableConfigEntry = (hass: HomeAssistant, configEntryId: string) =>
export const sortConfigEntries = (
configEntries: ConfigEntry[],
manifestLookup: { [domain: string]: IntegrationManifest }
primaryConfigEntry: string | null
): ConfigEntry[] => {
const sortedConfigEntries = [...configEntries];
const getScore = (entry: ConfigEntry) => {
const manifest = manifestLookup[entry.domain] as
| IntegrationManifest
| undefined;
const isHelper = manifest?.integration_type === "helper";
return isHelper ? -1 : 1;
};
const configEntriesCompare = (a: ConfigEntry, b: ConfigEntry) =>
getScore(b) - getScore(a);
return sortedConfigEntries.sort(configEntriesCompare);
if (!primaryConfigEntry) {
return configEntries;
}
const primaryEntry = configEntries.find(
(e) => e.entry_id === primaryConfigEntry
);
if (!primaryEntry) {
return configEntries;
}
const otherEntries = configEntries.filter(
(e) => e.entry_id !== primaryConfigEntry
);
return [primaryEntry, ...otherEntries];
};

View File

@@ -33,6 +33,7 @@ export interface DeviceRegistryEntry extends RegistryEntry {
entry_type: "service" | null;
disabled_by: "user" | "integration" | "config_entry" | null;
configuration_url: string | null;
primary_config_entry: string | null;
}
export interface DeviceEntityDisplayLookup {

View File

@@ -6,6 +6,7 @@ export interface LovelaceBaseSectionConfig {
title?: string;
visibility?: Condition[];
column_span?: number;
row_span?: number;
}
export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig {

View File

@@ -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 (
hass.localize(

View File

@@ -650,6 +650,7 @@ export class HaVoiceCommandDialog extends LitElement {
margin-inline-end: -24px;
margin-inline-start: initial;
direction: var(--direction);
transform: scaleX(var(--scale-direction));
}
.listening-icon[active] {

View File

@@ -153,7 +153,7 @@ export class HaConfigDevicePage extends LitElement {
.filter((entId) => entId in entryLookup)
.map((entry) => entryLookup[entry]);
return sortConfigEntries(deviceEntries, manifestLookup);
return sortConfigEntries(deviceEntries, device.primary_config_entry);
}
);

View File

@@ -388,7 +388,7 @@ export class HaConfigDeviceDashboard extends SubscribeMixin(LitElement) {
device.config_entries
.filter((entId) => entId in entryLookup)
.map((entId) => entryLookup[entId]),
manifestLookup
device.primary_config_entry
);
const labels = labelReg && device?.labels;

View File

@@ -6,19 +6,18 @@ import {
mdiCloseCircle,
mdiProgressClock,
} from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResultGroup,
html,
LitElement,
nothing,
PropertyValues,
TemplateResult,
css,
html,
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
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-card";
import "../../../../../components/ha-icon-next";
@@ -27,25 +26,19 @@ import "../../../../../components/ha-settings-row";
import "../../../../../components/ha-svg-icon";
import "../../../../../components/ha-switch";
import "../../../../../components/ha-textfield";
import { groupBy } from "../../../../../common/util/group-by";
import {
computeDeviceName,
DeviceRegistryEntry,
subscribeDeviceRegistry,
} from "../../../../../data/device_registry";
import { computeDeviceName } from "../../../../../data/device_registry";
import {
ZWaveJSNodeConfigParam,
ZWaveJSNodeConfigParams,
ZWaveJSSetConfigParamResult,
ZwaveJSNodeMetadata,
fetchZwaveNodeConfigParameters,
fetchZwaveNodeMetadata,
setZwaveNodeConfigParameter,
ZWaveJSNodeConfigParam,
ZWaveJSNodeConfigParams,
ZwaveJSNodeMetadata,
ZWaveJSSetConfigParamResult,
} from "../../../../../data/zwave_js";
import "../../../../../layouts/hass-error-screen";
import "../../../../../layouts/hass-loading-screen";
import "../../../../../layouts/hass-tabs-subpage";
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant, Route } from "../../../../../types";
import "../../../ha-config-section";
@@ -57,16 +50,8 @@ const icons = {
error: mdiCloseCircle,
};
const getDevice = memoizeOne(
(
deviceId: string,
entries?: DeviceRegistryEntry[]
): DeviceRegistryEntry | undefined =>
entries?.find((device) => device.id === deviceId)
);
@customElement("zwave_js-node-config")
class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
class ZWaveJSNodeConfig extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public route!: Route;
@@ -79,8 +64,6 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
@property() public deviceId!: string;
@state() private _deviceRegistryEntries?: DeviceRegistryEntry[];
@state() private _nodeMetadata?: ZwaveJSNodeMetadata;
@state() private _config?: ZWaveJSNodeConfigParams;
@@ -94,19 +77,8 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
this.deviceId = this.route.path.substr(1);
}
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeDeviceRegistry(this.hass.connection, (entries) => {
this._deviceRegistryEntries = entries;
}),
];
}
protected updated(changedProps: PropertyValues): void {
if (
(!this._config || changedProps.has("deviceId")) &&
changedProps.has("_deviceRegistryEntries")
) {
if (!this._config || changedProps.has("deviceId")) {
this._fetchData();
}
}
@@ -125,7 +97,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
return html`<hass-loading-screen></hass-loading-screen>`;
}
const device = this._device!;
const device = this.hass.devices[this.deviceId];
return html`
<hass-tabs-subpage
@@ -303,6 +275,11 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
.disabled=${!item.metadata.writeable}
@change=${this._numericInputChanged}
.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 }
)}
helperPersistent
>
</ha-textfield>`;
}
@@ -384,6 +361,19 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
if (Number(this._config![ev.target.key].value) === value) {
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._updateConfigParameter(ev.target, value);
}
@@ -392,7 +382,7 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
try {
const result = await setZwaveNodeConfigParameter(
this.hass,
this._device!.id,
this.deviceId,
target.property,
target.endpoint,
value,
@@ -420,16 +410,12 @@ class ZWaveJSNodeConfig extends SubscribeMixin(LitElement) {
this._results = { ...this._results, [key]: errorParam };
}
private get _device(): DeviceRegistryEntry | undefined {
return getDevice(this.deviceId, this._deviceRegistryEntries);
}
private async _fetchData() {
if (!this.configEntryId || !this._deviceRegistryEntries) {
if (!this.configEntryId) {
return;
}
const device = this._device;
const device = this.hass.devices[this.deviceId];
if (!device) {
this._error = "device_not_found";
return;

View File

@@ -250,7 +250,7 @@ export class HaConfigLogs extends LitElement {
--mdc-theme-primary: var(--primary-text-color);
--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-start: 8px;
}

View File

@@ -3,7 +3,6 @@ import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
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 { stateActive } from "../../../common/entity/state_active";
import { stateColorCss } from "../../../common/entity/state_color";
import "../../../components/ha-badge";
import "../../../components/ha-ripple";
import "../../../components/ha-state-icon";
import "../../../components/ha-svg-icon";
@@ -160,15 +160,14 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
if (!stateObj) {
return html`
<div class="badge error">
<ha-svg-icon .hass=${this.hass} .path=${mdiAlertCircle}></ha-svg-icon>
<span class="info">
<span class="label">${entityId}</span>
<span class="content">
${this.hass.localize("ui.badge.entity.not_found")}
</span>
</span>
</div>
<ha-badge .label=${entityId} class="error">
<ha-svg-icon
slot="icon"
.hass=${this.hass}
.path=${mdiAlertCircle}
></ha-svg-icon>
${this.hass.localize("ui.badge.entity.not_found")}
</ha-badge>
`;
}
@@ -204,42 +203,32 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
const content = showState ? stateDisplay : showName ? name : undefined;
return html`
<div
style=${styleMap(style)}
class="badge ${classMap({
active,
"no-info": !showState && !showName,
"no-icon": !showIcon,
})}"
<ha-badge
.type=${this.hasAction ? "button" : "badge"}
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config!.hold_action),
hasDoubleClick: hasAction(this._config!.double_tap_action),
})}
role=${ifDefined(this.hasAction ? "button" : undefined)}
tabindex=${ifDefined(this.hasAction ? "0" : undefined)}
.label=${label}
.iconOnly=${!content}
style=${styleMap(style)}
class=${classMap({ active })}
>
<ha-ripple .disabled=${!this.hasAction}></ha-ripple>
${showIcon
? imageUrl
? html`<img src=${imageUrl} aria-hidden />`
? html`<img slot="icon" src=${imageUrl} aria-hidden />`
: html`
<ha-state-icon
slot="icon"
.hass=${this.hass}
.stateObj=${stateObj}
.icon=${this._config.icon}
></ha-state-icon>
`
: nothing}
${content
? html`
<span class="info">
${label ? html`<span class="label">${name}</span>` : nothing}
<span class="content">${content}</span>
</span>
`
: nothing}
</div>
${content}
</ha-badge>
`;
}
@@ -249,119 +238,15 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
static get styles(): CSSResultGroup {
return css`
:host {
ha-badge {
--badge-color: var(--state-inactive-color);
-webkit-tap-highlight-color: transparent;
}
.badge.error {
ha-badge.error {
--badge-color: var(--red-color);
}
.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 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 {
ha-badge.active {
--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;
}
`;
}
}

View File

@@ -2,12 +2,11 @@ import { mdiAlertCircle } from "@mdi/js";
import { dump } from "js-yaml";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, state } from "lit/decorators";
import "../../../components/ha-label-badge";
import "../../../components/ha-badge";
import "../../../components/ha-svg-icon";
import { HomeAssistant } from "../../../types";
import { showAlertDialog } from "../custom-card-helpers";
import { LovelaceBadge } from "../types";
import { HuiEntityBadge } from "./hui-entity-badge";
import { ErrorBadgeConfig } from "./types";
export const createErrorBadgeElement = (config) => {
@@ -55,41 +54,36 @@ export class HuiErrorBadge extends LitElement implements LovelaceBadge {
}
return html`
<button class="badge error" @click=${this._viewDetail}>
<ha-svg-icon .hass=${this.hass} .path=${mdiAlertCircle}></ha-svg-icon>
<ha-ripple></ha-ripple>
<span class="content">
<span class="name">Error</span>
<span class="state">${this._config.error}</span>
</span>
</button>
<ha-badge
class="error"
@click=${this._viewDetail}
type="button"
label="Error"
>
<ha-svg-icon slot="icon" .path=${mdiAlertCircle}></ha-svg-icon>
<div class="content">${this._config.error}</div>
</ha-badge>
`;
}
static get styles(): CSSResultGroup {
return [
HuiEntityBadge.styles,
css`
.badge.error {
--badge-color: var(--error-color);
border-color: var(--badge-color);
}
ha-svg-icon {
color: var(--badge-color);
}
.state {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
pre {
font-family: var(--code-font-family, monospace);
white-space: break-spaces;
user-select: text;
}
`,
];
return css`
ha-badge {
--badge-color: var(--error-color);
--ha-card-border-color: var(--error-color);
}
.content {
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
pre {
font-family: var(--code-font-family, monospace);
white-space: break-spaces;
user-select: text;
}
`;
}
}

View File

@@ -6,14 +6,13 @@ import {
html,
nothing,
} from "lit";
import { mdiChevronRight } from "@mdi/js";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../components/chart/state-history-charts";
import "../../../components/ha-alert";
import "../../../components/ha-card";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-next";
import {
HistoryResult,
computeHistory,
@@ -209,9 +208,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
? html`
<h1 class="card-header">
${this._config.title}
<a href=${configUrl}
><ha-icon-button .path=${mdiChevronRight}></ha-icon-button
></a>
<a href=${configUrl}><ha-icon-next></ha-icon-next></a>
</h1>
`
: nothing}
@@ -258,7 +255,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
justify-content: space-between;
display: flex;
}
.card-header ha-icon-button {
.card-header ha-icon-next {
--mdc-icon-button-size: 24px;
line-height: 24px;
color: var(--primary-text-color);

View File

@@ -0,0 +1,25 @@
import { LocalizeFunc } from "../../../common/translations/localize";
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import {
getCustomCardEntry,
isCustomType,
stripCustomPrefix,
} from "../../../data/lovelace_custom_cards";
export const computeCardName = (
config: LovelaceCardConfig,
localize: LocalizeFunc
): string | undefined => {
if (isCustomType(config.type)) {
// prettier-ignore
let cardName = getCustomCardEntry(
stripCustomPrefix(config.type)
)?.name;
// Trim names that end in " Card" so as not to redundantly duplicate it
if (cardName?.toLowerCase().endsWith(" card")) {
cardName = cardName.substring(0, cardName.length - 5);
}
return cardName;
}
return localize(`ui.panel.lovelace.editor.card.${config.type}.name`);
};

View File

@@ -0,0 +1,155 @@
import "@material/mwc-tab-bar/mwc-tab-bar";
import "@material/mwc-tab/mwc-tab";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event";
import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import { LovelaceConfig } from "../../../../data/lovelace/config/types";
import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
import { HomeAssistant } from "../../../../types";
import "./hui-card-element-editor";
import type { HuiCardElementEditor } from "./hui-card-element-editor";
import "./hui-card-layout-editor";
import "./hui-card-visibility-editor";
const TABS = ["config", "visibility", "layout"] as const;
@customElement("hui-card-editor")
class HuiCardEditor extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public lovelace!: LovelaceConfig;
@property({ attribute: false }) public config!: LovelaceCardConfig;
@property({ attribute: false }) public containerConfig!:
| LovelaceViewConfig
| LovelaceSectionConfig;
@query("hui-card-element-editor")
public elementEditor?: HuiCardElementEditor;
@state() private _selectedTab: (typeof TABS)[number] = TABS[0];
private _tabs = memoizeOne(
(containerType: string | undefined, cardType: string) =>
TABS.filter((tab) => {
if (tab === "visibility") return cardType !== "conditional";
if (tab === "layout") return containerType === "grid";
return true;
})
);
private _elementConfig = memoizeOne((config: LovelaceCardConfig) => {
const { visibility, layout_options, ...elementConfig } = config;
return elementConfig;
});
private renderContent() {
if (this._selectedTab === "config") {
return html`
<hui-card-element-editor
.hass=${this.hass}
.lovelace=${this.lovelace}
.value=${this._elementConfig(this.config)}
show-toggle-mode-button
@config-changed=${this._elementConfigChanged}
></hui-card-element-editor>
`;
}
if (this._selectedTab === "visibility") {
return html`
<hui-card-visibility-editor
.hass=${this.hass}
.config=${this.config}
@value-changed=${this._configChanged}
></hui-card-visibility-editor>
`;
}
if (this._selectedTab === "layout") {
return html`
<hui-card-layout-editor
.hass=${this.hass}
.config=${this.config}
.sectionConfig=${this.containerConfig as LovelaceSectionConfig}
@value-changed=${this._configChanged}
>
</hui-card-layout-editor>
`;
}
return nothing;
}
private _configChanged(ev: CustomEvent): void {
ev.stopPropagation();
fireEvent(this, "config-changed", { config: ev.detail.value });
}
private _elementConfigChanged(ev: CustomEvent): void {
ev.stopPropagation();
const config = ev.detail.config;
const newConfig = {
...config,
visibility: this.config.visibility,
layout_options: this.config.layout_options,
};
fireEvent(this, "config-changed", { config: newConfig });
}
protected render() {
const cardType = this.config.type;
const containerType = this.containerConfig.type;
const tabs = this._tabs(containerType, cardType);
if (tabs.length <= 1) {
return this.renderContent();
}
return html`
<mwc-tab-bar
.activeIndex=${tabs.indexOf(this._selectedTab)}
@MDCTabBar:activated=${this._handleTabChanged}
>
${tabs.map(
(tab) => html`
<mwc-tab
.label=${this.hass.localize(
`ui.panel.lovelace.editor.edit_card.tab_${tab}`
)}
>
</mwc-tab>
`
)}
</mwc-tab-bar>
${this.renderContent()}
`;
}
private _handleTabChanged(ev: CustomEvent): void {
const cardType = this.config.type;
const containerType = this.containerConfig.type;
const tabs = this._tabs(containerType, cardType);
const newTab = tabs[ev.detail.index];
if (newTab === this._selectedTab) {
return;
}
this._selectedTab = newTab;
}
static get styles(): CSSResultGroup {
return css`
mwc-tab-bar {
text-transform: uppercase;
margin-bottom: 16px;
border-bottom: 1px solid var(--divider-color);
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-card-editor": HuiCardEditor;
}
}

View File

@@ -1,26 +1,11 @@
import "@material/mwc-tab-bar/mwc-tab-bar";
import "@material/mwc-tab/mwc-tab";
import { CSSResultGroup, TemplateResult, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { customElement } from "lit/decorators";
import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { getCardElementClass } from "../../create-element/create-card-element";
import type { LovelaceCardEditor, LovelaceConfigForm } from "../../types";
import { HuiElementEditor } from "../hui-element-editor";
import "./hui-card-layout-editor";
import "./hui-card-visibility-editor";
import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
const tabs = ["config", "visibility", "layout"] as const;
@customElement("hui-card-element-editor")
export class HuiCardElementEditor extends HuiElementEditor<LovelaceCardConfig> {
@property({ type: Boolean, attribute: "show-visibility-tab" })
public showVisibilityTab = false;
@property({ attribute: false }) public sectionConfig?: LovelaceSectionConfig;
@state() private _currTab: (typeof tabs)[number] = tabs[0];
protected async getConfigElement(): Promise<LovelaceCardEditor | undefined> {
const elClass = await getCardElementClass(this.configElementType!);
@@ -42,93 +27,6 @@ export class HuiCardElementEditor extends HuiElementEditor<LovelaceCardConfig> {
return undefined;
}
private _configChanged(ev: CustomEvent): void {
ev.stopPropagation();
this.value = ev.detail.value;
}
get _showLayoutTab(): boolean {
return (
!!this.sectionConfig &&
(this.sectionConfig.type === undefined ||
this.sectionConfig.type === "grid")
);
}
protected renderConfigElement(): TemplateResult {
const displayedTabs: string[] = ["config"];
if (this.showVisibilityTab) displayedTabs.push("visibility");
if (this._showLayoutTab) displayedTabs.push("layout");
if (displayedTabs.length === 1) return super.renderConfigElement();
let content: TemplateResult<1> | typeof nothing = nothing;
switch (this._currTab) {
case "config":
content = html`${super.renderConfigElement()}`;
break;
case "visibility":
content = html`
<hui-card-visibility-editor
.hass=${this.hass}
.config=${this.value}
@value-changed=${this._configChanged}
></hui-card-visibility-editor>
`;
break;
case "layout":
content = html`
<hui-card-layout-editor
.hass=${this.hass}
.config=${this.value}
.sectionConfig=${this.sectionConfig!}
@value-changed=${this._configChanged}
>
</hui-card-layout-editor>
`;
}
return html`
<mwc-tab-bar
.activeIndex=${tabs.indexOf(this._currTab)}
@MDCTabBar:activated=${this._handleTabChanged}
>
${displayedTabs.map(
(tab) => html`
<mwc-tab
.label=${this.hass.localize(
`ui.panel.lovelace.editor.edit_card.tab_${tab}`
)}
>
</mwc-tab>
`
)}
</mwc-tab-bar>
${content}
`;
}
private _handleTabChanged(ev: CustomEvent): void {
const newTab = tabs[ev.detail.index];
if (newTab === this._currTab) {
return;
}
this._currTab = newTab;
}
static get styles(): CSSResultGroup {
return [
HuiElementEditor.styles,
css`
mwc-tab-bar {
text-transform: uppercase;
margin-bottom: 16px;
border-bottom: 1px solid var(--divider-color);
}
`,
];
}
}
declare global {

View File

@@ -1,5 +1,14 @@
import { mdiClose, mdiHelpCircle } from "@mdi/js";
import "@material/mwc-list";
import "@material/web/divider/divider";
import {
mdiCheck,
mdiClose,
mdiDotsVertical,
mdiHelpCircle,
mdiOpenInNew,
} from "@mdi/js";
import deepFreeze from "deep-freeze";
import { dump, load } from "js-yaml";
import {
CSSResultGroup,
LitElement,
@@ -12,32 +21,30 @@ import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeRTLDirection } from "../../../../common/util/compute_rtl";
import "../../../../components/ha-button";
import "../../../../components/ha-button-menu-new";
import "../../../../components/ha-circular-progress";
import "../../../../components/ha-code-editor";
import "../../../../components/ha-dialog";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-menu-item";
import { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import { LovelaceSectionConfig } from "../../../../data/lovelace/config/section";
import { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
import {
getCustomCardEntry,
isCustomType,
stripCustomPrefix,
} from "../../../../data/lovelace_custom_cards";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
import "../../cards/hui-card";
import { computeCardName } from "../../common/compute-card-name";
import "../../sections/hui-section";
import { addCard, replaceCard } from "../config-util";
import { getCardDocumentationURL } from "../get-dashboard-documentation-url";
import type { ConfigChangedEvent } from "../hui-element-editor";
import { findLovelaceContainer } from "../lovelace-path";
import type { GUIModeChangedEvent } from "../types";
import "./hui-card-element-editor";
import "./hui-card-editor";
import type { HuiCardElementEditor } from "./hui-card-element-editor";
import type { EditCardDialogParams } from "./show-edit-card-dialog";
@@ -73,12 +80,10 @@ export class HuiDialogEditCard
@state() private _error?: string;
@state() private _guiModeAvailable? = true;
@query("hui-card-element-editor")
@query("hui-card-editor")
private _cardEditorEl?: HuiCardElementEditor;
@state() private _GUImode = true;
@state() private _yamlMode = false;
@state() private _documentationURL?: string;
@@ -88,8 +93,7 @@ export class HuiDialogEditCard
public async showDialog(params: EditCardDialogParams): Promise<void> {
this._params = params;
this._GUImode = true;
this._guiModeAvailable = true;
this._yamlMode = false;
const containerConfig = findLovelaceContainer(
params.lovelaceConfig,
@@ -168,21 +172,7 @@ export class HuiDialogEditCard
let heading: string;
if (this._cardConfig && this._cardConfig.type) {
let cardName: string | undefined;
if (isCustomType(this._cardConfig.type)) {
// prettier-ignore
cardName = getCustomCardEntry(
stripCustomPrefix(this._cardConfig.type)
)?.name;
// Trim names that end in " Card" so as not to redundantly duplicate it
if (cardName?.toLowerCase().endsWith(" card")) {
cardName = cardName.substring(0, cardName.length - 5);
}
} else {
cardName = this.hass!.localize(
`ui.panel.lovelace.editor.card.${this._cardConfig.type}.name`
);
}
const cardName = computeCardName(this._cardConfig, this.hass!.localize);
heading = this.hass!.localize(
"ui.panel.lovelace.editor.edit_card.typed_header",
{ type: cardName }
@@ -218,36 +208,99 @@ export class HuiDialogEditCard
.path=${mdiClose}
></ha-icon-button>
<span slot="title" @click=${this._enlarge}>${heading}</span>
${this._documentationURL !== undefined
? html`
<a
slot="actionItems"
href=${this._documentationURL}
title=${this.hass!.localize("ui.panel.lovelace.menu.help")}
target="_blank"
rel="noreferrer"
dir=${computeRTLDirection(this.hass)}
>
<ha-icon-button .path=${mdiHelpCircle}></ha-icon-button>
</a>
`
: nothing}
<ha-button-menu-new
slot="actionItems"
anchor-corner="end-end"
menu-corner="start-end"
>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
>
</ha-icon-button>
<ha-menu-item @click=${this._enableGuiMode}>
${!this._yamlMode
? html`
<ha-svg-icon
class="selected_menu_item"
slot="start"
.path=${mdiCheck}
></ha-svg-icon>
`
: html`<span class="blank-icon" slot="start"></span>`}
<div slot="headline">
${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.edit_ui"
)}
</div>
</ha-menu-item>
<ha-menu-item @click=${this._enableYamlMode}>
${this._yamlMode
? html`
<ha-svg-icon
class="selected_menu_item"
slot="start"
.path=${mdiCheck}
></ha-svg-icon>
`
: html`<span class="blank-icon" slot="start"></span>`}
<div slot="headline">
${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.edit_yaml"
)}
</div>
</ha-menu-item>
${this._documentationURL !== undefined
? html`
<md-divider role="separator" tabindex="-1"></md-divider>
<ha-menu-item
type="link"
href=${this._documentationURL}
target="_blank"
rel="noreferrer"
>
<ha-svg-icon
slot="start"
.path=${mdiHelpCircle}
></ha-svg-icon>
<div slot="headline">
${this.hass!.localize("ui.panel.lovelace.menu.help")}
</div>
<ha-svg-icon slot="end" .path=${mdiOpenInNew}></ha-svg-icon>
</ha-menu-item>
`
: nothing}
</ha-button-menu-new>
</ha-dialog-header>
<div class="content">
<div class="element-editor">
<hui-card-element-editor
.showVisibilityTab=${this._cardConfig?.type !== "conditional"}
.sectionConfig=${this._isInSection
? this._containerConfig
: undefined}
.hass=${this.hass}
.lovelace=${this._params.lovelaceConfig}
.value=${this._cardConfig}
@config-changed=${this._handleConfigChanged}
@GUImode-changed=${this._handleGUIModeChanged}
@editor-save=${this._save}
dialogInitialFocus
></hui-card-element-editor>
${this._yamlMode
? html`
<ha-code-editor
mode="yaml"
autofocus
autocomplete-entities
autocomplete-icons
.hass=${this.hass}
.value=${dump(this._cardConfig)}
@value-changed=${this._handleYAMLChanged}
@keydown=${this._ignoreKeydown}
dir="ltr"
></ha-code-editor>
`
: html`
<hui-card-editor
.containerConfig=${this._containerConfig}
.hass=${this.hass}
.lovelace=${this._params.lovelaceConfig}
.config=${this._cardConfig}
@config-changed=${this._handleConfigChanged}
@editor-save=${this._save}
dialogInitialFocus
>
</hui-card-editor>
`}
</div>
<div class="element-preview">
${this._isInSection
@@ -277,49 +330,44 @@ export class HuiDialogEditCard
: ``}
</div>
</div>
${this._cardConfig !== undefined
<ha-button
@click=${this._cancel}
slot="secondaryAction"
dialogInitialFocus
>
${this.hass!.localize("ui.common.cancel")}
</ha-button>
${this._cardConfig !== undefined && this._dirty
? html`
<mwc-button
slot="secondaryAction"
@click=${this._toggleMode}
.disabled=${!this._guiModeAvailable}
class="gui-mode-button"
<ha-button
slot="primaryAction"
?disabled=${!this._canSave || this._saving}
@click=${this._save}
>
${this.hass!.localize(
!this._cardEditorEl || this._GUImode
? "ui.panel.lovelace.editor.edit_card.show_code_editor"
: "ui.panel.lovelace.editor.edit_card.show_visual_editor"
)}
</mwc-button>
${this._saving
? html`
<ha-circular-progress
indeterminate
aria-label="Saving"
size="small"
></ha-circular-progress>
`
: this.hass!.localize("ui.common.save")}
</ha-button>
`
: ""}
<div slot="primaryAction" @click=${this._save}>
<mwc-button @click=${this._cancel} dialogInitialFocus>
${this.hass!.localize("ui.common.cancel")}
</mwc-button>
${this._cardConfig !== undefined && this._dirty
? html`
<mwc-button
?disabled=${!this._canSave || this._saving}
@click=${this._save}
>
${this._saving
? html`
<ha-circular-progress
indeterminate
aria-label="Saving"
size="small"
></ha-circular-progress>
`
: this.hass!.localize("ui.common.save")}
</mwc-button>
`
: ``}
</div>
: nothing}
</ha-dialog>
`;
}
private _enableGuiMode() {
this._yamlMode = false;
}
private _enableYamlMode() {
this._yamlMode = true;
}
private _enlarge() {
this.large = !this.large;
}
@@ -328,27 +376,21 @@ export class HuiDialogEditCard
ev.stopPropagation();
}
private _handleConfigChanged(ev: HASSDomEvent<ConfigChangedEvent>) {
this._cardConfig = deepFreeze(ev.detail.config);
this._error = ev.detail.error;
this._guiModeAvailable = ev.detail.guiModeAvailable;
private _handleYAMLChanged(ev: CustomEvent) {
this._cardConfig = load(ev.detail.value) as LovelaceCardConfig;
this._dirty = true;
}
private _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
ev.stopPropagation();
this._GUImode = ev.detail.guiMode;
this._guiModeAvailable = ev.detail.guiModeAvailable;
}
private _toggleMode(): void {
this._cardEditorEl?.toggleMode();
private _handleConfigChanged(ev: HASSDomEvent<ConfigChangedEvent>) {
this._cardConfig = deepFreeze(ev.detail.config);
this._error = ev.detail.error;
this._dirty = true;
}
private _opened() {
window.addEventListener("dialog-closed", this._enableEscapeKeyClose);
window.addEventListener("hass-more-info", this._disableEscapeKeyClose);
this._cardEditorEl?.focusYamlEditor();
// this._cardEditorEl?.focusYamlEditor();
}
private get _isInSection() {
@@ -551,11 +593,6 @@ export class HuiDialogEditCard
width: 100%;
box-sizing: border-box;
}
.gui-mode-button {
margin-right: auto;
margin-inline-end: auto;
margin-inline-start: initial;
}
.header {
display: flex;
align-items: center;
@@ -565,6 +602,12 @@ export class HuiDialogEditCard
color: inherit;
text-decoration: none;
}
.selected_menu_item {
color: var(--primary-color);
}
.blank-icon {
width: 16px;
}
`,
];
}

View File

@@ -1,4 +1,4 @@
import "@material/mwc-button";
import { mdiCodeBraces, mdiListBox } from "@mdi/js";
import { dump, load } from "js-yaml";
import {
CSSResultGroup,
@@ -7,6 +7,7 @@ import {
TemplateResult,
css,
html,
nothing,
} from "lit";
import { property, query, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
@@ -16,14 +17,17 @@ import "../../../components/ha-alert";
import "../../../components/ha-circular-progress";
import "../../../components/ha-code-editor";
import type { HaCodeEditor } from "../../../components/ha-code-editor";
import "../../../components/ha-outlined-segmented-button";
import "../../../components/ha-outlined-segmented-button-set";
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
import { LovelaceConfig } from "../../../data/lovelace/config/types";
import type { HomeAssistant } from "../../../types";
import { LovelaceCardFeatureConfig } from "../card-features/types";
import { LovelaceElementConfig } from "../elements/types";
import type { LovelaceRowConfig } from "../entity-rows/types";
import { LovelaceHeaderFooterConfig } from "../header-footer/types";
import { LovelaceElementConfig } from "../elements/types";
import type {
LovelaceConfigForm,
LovelaceGenericElementEditor,
@@ -33,7 +37,6 @@ import type { HuiFormEditor } from "./config-elements/hui-form-editor";
import "./config-elements/hui-generic-entity-row-editor";
import { GUISupportError } from "./gui-support-error";
import { EditSubElementEvent, GUIModeChangedEvent } from "./types";
import { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
export interface ConfigChangedEvent {
config:
@@ -73,6 +76,9 @@ export abstract class HuiElementEditor<T, C = any> extends LitElement {
@property({ attribute: false }) public context?: C;
@property({ type: Boolean, attribute: "show-toggle-mode-button" })
public showToggleModeButton = false;
@state() private _yaml?: string;
@state() private _config?: T;
@@ -208,8 +214,40 @@ export abstract class HuiElementEditor<T, C = any> extends LitElement {
}
protected render(): TemplateResult {
const guiModeAvailable = !(
this.hasWarning ||
this.hasError ||
this._guiSupported === false
);
return html`
<div class="wrapper">
${this.showToggleModeButton
? html`
<div class="header">
<ha-outlined-segmented-button-set
@segmented-button-set-selection=${this._handleModeSelected}
>
<ha-outlined-segmented-button
.selected=${this._guiMode}
.disabled=${!guiModeAvailable}
no-checkmark
>
<ha-svg-icon slot="icon" .path=${mdiListBox}></ha-svg-icon>
</ha-outlined-segmented-button>
<ha-outlined-segmented-button
.selected=${!this._guiMode}
no-checkmark
>
<ha-svg-icon
slot="icon"
.path=${mdiCodeBraces}
></ha-svg-icon>
</ha-outlined-segmented-button>
</ha-outlined-segmented-button-set>
</div>
`
: nothing}
${this.GUImode
? html`
<div class="gui-editor">
@@ -241,43 +279,58 @@ export abstract class HuiElementEditor<T, C = any> extends LitElement {
`}
${this._guiSupported === false && this.configElementType
? html`
<div class="info">
${this.hass.localize("ui.errors.config.editor_not_available", {
type: this.configElementType,
})}
</div>
<ha-alert
alert-type="info"
.title=${this.hass.localize(
"ui.errors.config.editor_not_supported"
)}
>
${this.hass.localize(
"ui.errors.config.editor_not_supported_details",
{ type: this.configElementType }
)}
<br />
${this.hass.localize("ui.errors.config.edit_in_yaml_supported")}
</ha-alert>
`
: ""}
: nothing}
${this.hasError
? html`
<div class="error">
${this.hass.localize("ui.errors.config.error_detected")}:
<br />
<ha-alert
alert-type="error"
.title=${this.hass.localize(
"ui.errors.config.invalid_configuration"
)}
>
${this.hass.localize("ui.errors.config.error_details")}
<ul>
${this._errors!.map((error) => html`<li>${error}</li>`)}
</ul>
</div>
</ha-alert>
`
: ""}
: nothing}
${this.hasWarning
? html`
<ha-alert
alert-type="warning"
.title="${this.hass.localize(
.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>`
: ""}
? html`
${this.hass.localize("ui.errors.config.warning_details")}
<ul>
${this._warnings!.map(
(warning) => html`<li>${warning}</li>`
)}
</ul>
`
: nothing}
${this.hass.localize("ui.errors.config.edit_in_yaml_supported")}
</ha-alert>
`
: ""}
: nothing}
</div>
`;
}
@@ -311,6 +364,10 @@ export abstract class HuiElementEditor<T, C = any> extends LitElement {
this.value = config as unknown as T;
}
private _handleModeSelected(ev) {
this.GUImode = ev.detail.index === 0;
}
private _handleYAMLChanged(ev: CustomEvent) {
ev.stopPropagation();
const newYaml = ev.detail.value;
@@ -452,6 +509,10 @@ export abstract class HuiElementEditor<T, C = any> extends LitElement {
display: block;
margin: auto;
}
.header {
display: flex;
justify-content: flex-end;
}
`;
}
}

View File

@@ -12,6 +12,7 @@ import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { repeat } from "lit/directives/repeat";
import { styleMap } from "lit/directives/style-map";
import { clamp } from "../../../common/number/clamp";
import "../../../components/ha-icon-button";
import "../../../components/ha-sortable";
import "../../../components/ha-svg-icon";
@@ -56,6 +57,8 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
callback: (entries) => {
const totalWidth = entries[0]?.contentRect.width;
if (!totalWidth) return 1;
const style = getComputedStyle(this);
const container = this.shadowRoot!.querySelector(".container")!;
const containerStyle = getComputedStyle(container);
@@ -72,7 +75,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
(totalWidth - padding + columnGap) / (minColumnWidth + columnGap)
);
const maxColumns = this._config?.max_columns ?? DEFAULT_MAX_COLUMNS;
return Math.max(Math.min(maxColumns, columns), 1);
return clamp(columns, 1, maxColumns);
},
});
@@ -166,6 +169,8 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
maxColumnCount
);
const rowSpan = sectionConfig?.row_span || 1;
(section as any).itemPath = [idx];
return html`
@@ -173,6 +178,7 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
class="section"
style=${styleMap({
"--column-span": columnSpan,
"--row-span": rowSpan,
})}
>
${
@@ -332,9 +338,10 @@ export class SectionsView extends LitElement implements LovelaceViewElement {
.section {
border-radius: var(--ha-card-border-radius, 12px);
grid-column: span var(--column-span);
grid-row: span var(--row-span);
}
.section:not(:has(> *:not([hidden]))) {
.section:has(hui-section[hidden]) {
display: none;
}

View File

@@ -1797,10 +1797,13 @@
"errors": {
"config": {
"no_type_provided": "No type provided.",
"error_detected": "Configuration errors detected",
"invalid_configuration": "Invalid configuration",
"error_details": "The configuration contains the following errors:",
"editor_not_available": "No visual editor available for type ''{type}''.",
"editor_not_supported": "Visual editor is not supported for this configuration",
"edit_in_yaml_supported": "You can still edit your config in YAML.",
"editor_not_supported": "Visual editor not supported",
"editor_not_supported_details": "The visual editor is not supported for type ''{type}''.",
"warning_details": "The configuration contains the following warnings:",
"edit_in_yaml_supported": "You can still edit the configuration in YAML.",
"key_missing": "Required key ''{key}'' is missing.",
"key_not_expected": "Key ''{key}'' is not expected or not supported by the visual editor.",
"key_wrong_type": "The provided value for ''{key}'' is not supported by the visual editor. We support ({type_correct}) but received ({type_wrong}).",
@@ -4895,6 +4898,8 @@
"zwave_js_device_database": "Z-Wave JS Device Database",
"battery_device_notice": "Battery devices must be awake to update their config. Please refer to your device manual for instructions on how to wake the device.",
"parameter_is_read_only": "This parameter is read-only.",
"between_min_max": "Between {min} and {max}",
"error_not_in_range": "Value must be between {min} and {max}",
"error_device_not_found": "Device not found",
"set_param_accepted": "The parameter has been updated.",
"set_param_queued": "The parameter change has been queued, and will be updated when the device wakes up.",

408
yarn.lock
View File

@@ -1379,12 +1379,12 @@ __metadata:
languageName: node
linkType: hard
"@babel/runtime@npm:7.25.4, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
version: 7.25.4
resolution: "@babel/runtime@npm:7.25.4"
"@babel/runtime@npm:7.25.6, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4":
version: 7.25.6
resolution: "@babel/runtime@npm:7.25.6"
dependencies:
regenerator-runtime: "npm:^0.14.0"
checksum: 10/70d2a420c24a3289ea6c4addaf3a1c4186bc3d001c92445faa3cd7601d7d2fbdb32c63b3a26b9771e20ff2f511fa76b726bf256f823cdb95bc37b8eadbd02f70
checksum: 10/0c4134734deb20e1005ffb9165bf342e1074576621b246d8e5e41cc7cb315a885b7d98950fbf5c63619a2990a56ae82f444d35fe8c4691a0b70c2fe5673667dc
languageName: node
linkType: hard
@@ -1458,15 +1458,15 @@ __metadata:
languageName: node
linkType: hard
"@codemirror/commands@npm:6.6.0":
version: 6.6.0
resolution: "@codemirror/commands@npm:6.6.0"
"@codemirror/commands@npm:6.6.1":
version: 6.6.1
resolution: "@codemirror/commands@npm:6.6.1"
dependencies:
"@codemirror/language": "npm:^6.0.0"
"@codemirror/state": "npm:^6.4.0"
"@codemirror/view": "npm:^6.27.0"
"@lezer/common": "npm:^1.1.0"
checksum: 10/e984511b8ac06a1afe01005a50af7a0b6765cf350547d281a13aedbcccbc8620187c6dce55caa0f7c8ba409771e4abb4aa3bae824b6870249c8176b57cc4a42e
checksum: 10/533f33db08a4ff7e400ff61e48d34525afdc894f92442a5bf469ed24b1992a61bf726b61647e2faa552a2f4703c149c92026d9a1f9169f615582cdfc36fcb010
languageName: node
linkType: hard
@@ -3889,6 +3889,13 @@ __metadata:
languageName: node
linkType: hard
"@rtsao/scc@npm:^1.1.0":
version: 1.1.0
resolution: "@rtsao/scc@npm:1.1.0"
checksum: 10/17d04adf404e04c1e61391ed97bca5117d4c2767a76ae3e879390d6dec7b317fcae68afbf9e98badee075d0b64fa60f287729c4942021b4d19cd01db77385c01
languageName: node
linkType: hard
"@sinonjs/commons@npm:^2.0.0":
version: 2.0.0
resolution: "@sinonjs/commons@npm:2.0.0"
@@ -4661,129 +4668,129 @@ __metadata:
languageName: node
linkType: hard
"@vaadin/a11y-base@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/a11y-base@npm:24.4.6"
"@vaadin/a11y-base@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/a11y-base@npm:24.4.7"
dependencies:
"@open-wc/dedupe-mixin": "npm:^1.3.0"
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/component-base": "npm:~24.4.7"
lit: "npm:^3.0.0"
checksum: 10/46acdd62f5485b7b3dce8e746123a58fe4c6c619d51d6fdf5c3d1be52eb5939b22161c1fc321f2c69682950c79326f97ec5684dfd83b3ab09b7d65a16035647f
checksum: 10/a6b73c3ad697c5b511e0695004f1a1d1bcea025be77f277a5b1a525c686f8b91d1b02930d4318283d30849c4afdedf57b955540f43c80ff43a4392460b3603dd
languageName: node
linkType: hard
"@vaadin/combo-box@npm:24.4.6":
version: 24.4.6
resolution: "@vaadin/combo-box@npm:24.4.6"
"@vaadin/combo-box@npm:24.4.7":
version: 24.4.7
resolution: "@vaadin/combo-box@npm:24.4.7"
dependencies:
"@open-wc/dedupe-mixin": "npm:^1.3.0"
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/a11y-base": "npm:~24.4.6"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/field-base": "npm:~24.4.6"
"@vaadin/input-container": "npm:~24.4.6"
"@vaadin/item": "npm:~24.4.6"
"@vaadin/lit-renderer": "npm:~24.4.6"
"@vaadin/overlay": "npm:~24.4.6"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.6"
"@vaadin/vaadin-material-styles": "npm:~24.4.6"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.6"
checksum: 10/437cf54c1198f347786ef864cd77a52ac9bc9f4dba527c25c678c5a5ca5c0c41e8359db1d24430a053cb70b69fde68a061112247ed6b8ee1ffb8859937624a28
"@vaadin/a11y-base": "npm:~24.4.7"
"@vaadin/component-base": "npm:~24.4.7"
"@vaadin/field-base": "npm:~24.4.7"
"@vaadin/input-container": "npm:~24.4.7"
"@vaadin/item": "npm:~24.4.7"
"@vaadin/lit-renderer": "npm:~24.4.7"
"@vaadin/overlay": "npm:~24.4.7"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.7"
"@vaadin/vaadin-material-styles": "npm:~24.4.7"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.7"
checksum: 10/d99b9f7261279b75493de192c555e2125edd22544bbe5d4acdd1859c4f0b942848c9655b72dab8e5d85e5103b3c72425a43482acbfc75be70a20dd00908281a4
languageName: node
linkType: hard
"@vaadin/component-base@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/component-base@npm:24.4.6"
"@vaadin/component-base@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/component-base@npm:24.4.7"
dependencies:
"@open-wc/dedupe-mixin": "npm:^1.3.0"
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/vaadin-development-mode-detector": "npm:^2.0.0"
"@vaadin/vaadin-usage-statistics": "npm:^2.1.0"
lit: "npm:^3.0.0"
checksum: 10/3849183ed62c6742cba4923ecc6d49edede0d85b5d4edd7c9efb37ee260468072065d9cf3e66b741de01f53662e0551049069ee79caf65b220d5db1e932ebe5e
checksum: 10/c84bdcdf85ec46717e575af1c6d570cd81bbc22524024d769fc427df79cad603b1c3aca8015ff0c764a61ed3da274242da3c100d2dfc1529e065fa35af389daa
languageName: node
linkType: hard
"@vaadin/field-base@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/field-base@npm:24.4.6"
"@vaadin/field-base@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/field-base@npm:24.4.7"
dependencies:
"@open-wc/dedupe-mixin": "npm:^1.3.0"
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/a11y-base": "npm:~24.4.6"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/a11y-base": "npm:~24.4.7"
"@vaadin/component-base": "npm:~24.4.7"
lit: "npm:^3.0.0"
checksum: 10/27fb01628a3ef83142c4de854e92940e8edcb136af9e75c4687fa627a017f6a36861069a22306581019b3ee9dce133e7b38b991886755f83ed0a359cebb51ab1
checksum: 10/8e83d40ef8d2ddd2171a3cb6426554043cd7ac5937643cfc63f6386b80cb84fe2610282d37b62a71c4f6957860d6baa24a08f201b740bd887eabf0c61e310178
languageName: node
linkType: hard
"@vaadin/icon@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/icon@npm:24.4.6"
"@vaadin/icon@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/icon@npm:24.4.7"
dependencies:
"@open-wc/dedupe-mixin": "npm:^1.3.0"
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.6"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.6"
"@vaadin/component-base": "npm:~24.4.7"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.7"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.7"
lit: "npm:^3.0.0"
checksum: 10/4baa72e80f6dd9c12e8c6a3681a90eb602289a5f3f1c90bd921a0c6904f5406abdc2034ef1fad66d7ddfbd8789ab66c26d4d43aedfbd135a97da09e3e5914fcf
checksum: 10/f094ad8137e63df193bdf3952d7419ac68bf02d2b0777598062a4e609568bffa931af5a16b07566b1ef09f88ba99e1b9d72d94cb13c04f69a4079c0a296fd684
languageName: node
linkType: hard
"@vaadin/input-container@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/input-container@npm:24.4.6"
"@vaadin/input-container@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/input-container@npm:24.4.7"
dependencies:
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.6"
"@vaadin/vaadin-material-styles": "npm:~24.4.6"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.6"
"@vaadin/component-base": "npm:~24.4.7"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.7"
"@vaadin/vaadin-material-styles": "npm:~24.4.7"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.7"
lit: "npm:^3.0.0"
checksum: 10/5ae6a9e951e7423127443dd5b1c990cc78be581c067787839dceb58927abac3a4615f8b9cf15b61d4b7f50d4ae603be5e402f943b21930d734d66e4924f869c4
checksum: 10/55e5e695724a5a1456fbfb8060dd855d82723d5bf22356e2387d7b88ad2f5778b5a84f3fbae144a6568922eb0476d7b972ee6dac0a635e64874fa50281766685
languageName: node
linkType: hard
"@vaadin/item@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/item@npm:24.4.6"
"@vaadin/item@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/item@npm:24.4.7"
dependencies:
"@open-wc/dedupe-mixin": "npm:^1.3.0"
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/a11y-base": "npm:~24.4.6"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.6"
"@vaadin/vaadin-material-styles": "npm:~24.4.6"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.6"
checksum: 10/628757ac9c28f6440dec7a73143e2bce5ae8b59d7840b3a8219d107eab3e835a5ae2725e0c957e922582d9566c760bc7360a5ebdad459a74added914c072a9ec
"@vaadin/a11y-base": "npm:~24.4.7"
"@vaadin/component-base": "npm:~24.4.7"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.7"
"@vaadin/vaadin-material-styles": "npm:~24.4.7"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.7"
checksum: 10/4f08a165ac81c065d4f2a305ee4d89d35a33fa30f584266ff2f230936bf3b75e144530a6ee9667be8a5325cd945bfd55fb2a36c28fd77baca4a656420206eb15
languageName: node
linkType: hard
"@vaadin/lit-renderer@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/lit-renderer@npm:24.4.6"
"@vaadin/lit-renderer@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/lit-renderer@npm:24.4.7"
dependencies:
lit: "npm:^3.0.0"
checksum: 10/e0063bf2a31caab3ff55414fa6937a864b494e6efe1178a24d17f7196d71e137fddf8adf458523f534f707f17f5e51b16b449b79c27c38b865c5c4eec97b5805
checksum: 10/1548e54ededd4c26aa0eef27c460c44379b1e6497571e157a1d8e2a58bb645a638d7a7952375848f977181bbcedfec115ba893181f87b7431d031c7d64c3db4b
languageName: node
linkType: hard
"@vaadin/overlay@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/overlay@npm:24.4.6"
"@vaadin/overlay@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/overlay@npm:24.4.7"
dependencies:
"@open-wc/dedupe-mixin": "npm:^1.3.0"
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/a11y-base": "npm:~24.4.6"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.6"
"@vaadin/vaadin-material-styles": "npm:~24.4.6"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.6"
"@vaadin/a11y-base": "npm:~24.4.7"
"@vaadin/component-base": "npm:~24.4.7"
"@vaadin/vaadin-lumo-styles": "npm:~24.4.7"
"@vaadin/vaadin-material-styles": "npm:~24.4.7"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.7"
lit: "npm:^3.0.0"
checksum: 10/cdd92fd6b01e643e638b03072fffa8542ee1f00ef3966e9b2c3fdc34309a94451dfeeac72c611356a1a63f2f0398ea7f6da2902afd095668001161d77fa0cd46
checksum: 10/761edb1b1defb30fd32132847a60b6fffc9673956a2344480a9b73c954477fce6e3689866c9998d8e6d52cd91db345fd0ff1e18e39c8cb79697b4f285a7cf68f
languageName: node
linkType: hard
@@ -4794,36 +4801,36 @@ __metadata:
languageName: node
linkType: hard
"@vaadin/vaadin-lumo-styles@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/vaadin-lumo-styles@npm:24.4.6"
"@vaadin/vaadin-lumo-styles@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/vaadin-lumo-styles@npm:24.4.7"
dependencies:
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/icon": "npm:~24.4.6"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.6"
checksum: 10/f1dd83c36850ecf3951e484e9d57b192b56a55c78e978f587ea5fb16607d4ef257013b04793a90791f85cf66b7bfa613edb208826a607fe1afb21fd39eb5491d
"@vaadin/component-base": "npm:~24.4.7"
"@vaadin/icon": "npm:~24.4.7"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.7"
checksum: 10/e792225fdf37a440c864158263df4b1a8315ddc7fce7d983bc74aeaf0a726b046e5688866d80c860936c64554c2ddb446de44242a65ebf1f27248e0a338bf02c
languageName: node
linkType: hard
"@vaadin/vaadin-material-styles@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/vaadin-material-styles@npm:24.4.6"
"@vaadin/vaadin-material-styles@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/vaadin-material-styles@npm:24.4.7"
dependencies:
"@polymer/polymer": "npm:^3.0.0"
"@vaadin/component-base": "npm:~24.4.6"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.6"
checksum: 10/c1add552163cf7e67633722d2eb9df48dce68b5519587befd84a3678e5f1972b5286f065613015c7b11961e96cf8b41266f0e1b0e63a2648df18ec14179f51aa
"@vaadin/component-base": "npm:~24.4.7"
"@vaadin/vaadin-themable-mixin": "npm:~24.4.7"
checksum: 10/2de8ba3be9def3c63c4d25dfec07db4df7efcab5155601788204a443762d78729218b37eb18854933a60d27981affd5f87168f82e3e6a6533db96e147a367f1f
languageName: node
linkType: hard
"@vaadin/vaadin-themable-mixin@npm:24.4.6, @vaadin/vaadin-themable-mixin@npm:~24.4.6":
version: 24.4.6
resolution: "@vaadin/vaadin-themable-mixin@npm:24.4.6"
"@vaadin/vaadin-themable-mixin@npm:24.4.7, @vaadin/vaadin-themable-mixin@npm:~24.4.7":
version: 24.4.7
resolution: "@vaadin/vaadin-themable-mixin@npm:24.4.7"
dependencies:
"@open-wc/dedupe-mixin": "npm:^1.3.0"
lit: "npm:^3.0.0"
checksum: 10/ae91d1f870e16f4296d8a7dee2ee6765a1e2036a4eb8e5189e86ec20307d9cf196eb7bb50c1f220450a2d74e67fe4899d1ce940c320ea5957164c4f27546c52b
checksum: 10/aef690ca7d4ab90da0e0e3b0cb4e12a03a27b93e13481023394c118d64388e44606072bd640beece35ba21dddd2b8417261ca1ab05934a270c7facf7f0827e54
languageName: node
linkType: hard
@@ -5627,7 +5634,7 @@ __metadata:
languageName: node
linkType: hard
"array-includes@npm:^3.1.7":
"array-includes@npm:^3.1.8":
version: 3.1.8
resolution: "array-includes@npm:3.1.8"
dependencies:
@@ -5655,20 +5662,7 @@ __metadata:
languageName: node
linkType: hard
"array.prototype.find@npm:^2.2.2":
version: 2.2.3
resolution: "array.prototype.find@npm:2.2.3"
dependencies:
call-bind: "npm:^1.0.7"
define-properties: "npm:^1.2.1"
es-abstract: "npm:^1.23.2"
es-object-atoms: "npm:^1.0.0"
es-shim-unscopables: "npm:^1.0.2"
checksum: 10/8ee81d37de9c8574a94f4773dffa40b4d200deca11b00f7176dcb328a9ddcf75fef117c97ccce1ab8345b7184c107553156908e7dcaf0d42f1a395a04bbe803e
languageName: node
linkType: hard
"array.prototype.findlastindex@npm:^1.2.3":
"array.prototype.findlastindex@npm:^1.2.5":
version: 1.2.5
resolution: "array.prototype.findlastindex@npm:1.2.5"
dependencies:
@@ -6929,15 +6923,6 @@ __metadata:
languageName: node
linkType: hard
"default-gateway@npm:^6.0.3":
version: 6.0.3
resolution: "default-gateway@npm:6.0.3"
dependencies:
execa: "npm:^5.0.0"
checksum: 10/126f8273ecac8ee9ff91ea778e8784f6cd732d77c3157e8c5bdd6ed03651b5291f71446d05bc02d04073b1e67583604db5394ea3cf992ede0088c70ea15b7378
languageName: node
linkType: hard
"defaults@npm:^1.0.3, defaults@npm:^1.0.4":
version: 1.0.4
resolution: "defaults@npm:1.0.4"
@@ -7521,11 +7506,10 @@ __metadata:
languageName: node
linkType: hard
"eslint-import-resolver-webpack@npm:0.13.8":
version: 0.13.8
resolution: "eslint-import-resolver-webpack@npm:0.13.8"
"eslint-import-resolver-webpack@npm:0.13.9":
version: 0.13.9
resolution: "eslint-import-resolver-webpack@npm:0.13.9"
dependencies:
array.prototype.find: "npm:^2.2.2"
debug: "npm:^3.2.7"
enhanced-resolve: "npm:^0.9.1"
find-root: "npm:^1.1.0"
@@ -7539,46 +7523,47 @@ __metadata:
peerDependencies:
eslint-plugin-import: ">=1.4.0"
webpack: ">=1.11.0"
checksum: 10/b4acdc76ea156d7b22639250c3bc92d88fe1c581e7e8320115319437002a770944f9232807660df9c28a12677450c4954f5d629761bfd04092084c2493a77aaf
checksum: 10/d359fa2cfe4a19b9a9c5e1d973e2fdc2e95e0374d8a43db1d7b31831fda7ebc8ae1937e8237c1125790d08380fe817a7256d92d7d7dfda128eea83454229a003
languageName: node
linkType: hard
"eslint-module-utils@npm:^2.8.0":
version: 2.8.1
resolution: "eslint-module-utils@npm:2.8.1"
"eslint-module-utils@npm:^2.9.0":
version: 2.11.0
resolution: "eslint-module-utils@npm:2.11.0"
dependencies:
debug: "npm:^3.2.7"
peerDependenciesMeta:
eslint:
optional: true
checksum: 10/3e7892c0a984c963632da56b30ccf8254c29b535467138f91086c2ecdb2ebd10e2be61b54e553f30e5abf1d14d47a7baa0dac890e3a658fd3cd07dca63afbe6d
checksum: 10/1ba42cf48c5f9ec3b76dfa42c16f1c24c10508313689425c05ccb1d0eaa34bdc5c5b9c0c033cd402e9c429666bd3eb8c6d0c66565b0c00949fae743ad3643c95
languageName: node
linkType: hard
"eslint-plugin-import@npm:2.29.1":
version: 2.29.1
resolution: "eslint-plugin-import@npm:2.29.1"
"eslint-plugin-import@npm:2.30.0":
version: 2.30.0
resolution: "eslint-plugin-import@npm:2.30.0"
dependencies:
array-includes: "npm:^3.1.7"
array.prototype.findlastindex: "npm:^1.2.3"
"@rtsao/scc": "npm:^1.1.0"
array-includes: "npm:^3.1.8"
array.prototype.findlastindex: "npm:^1.2.5"
array.prototype.flat: "npm:^1.3.2"
array.prototype.flatmap: "npm:^1.3.2"
debug: "npm:^3.2.7"
doctrine: "npm:^2.1.0"
eslint-import-resolver-node: "npm:^0.3.9"
eslint-module-utils: "npm:^2.8.0"
hasown: "npm:^2.0.0"
is-core-module: "npm:^2.13.1"
eslint-module-utils: "npm:^2.9.0"
hasown: "npm:^2.0.2"
is-core-module: "npm:^2.15.1"
is-glob: "npm:^4.0.3"
minimatch: "npm:^3.1.2"
object.fromentries: "npm:^2.0.7"
object.groupby: "npm:^1.0.1"
object.values: "npm:^1.1.7"
object.fromentries: "npm:^2.0.8"
object.groupby: "npm:^1.0.3"
object.values: "npm:^1.2.0"
semver: "npm:^6.3.1"
tsconfig-paths: "npm:^3.15.0"
peerDependencies:
eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
checksum: 10/5865f05c38552145423c535326ec9a7113ab2305c7614c8b896ff905cfabc859c8805cac21e979c9f6f742afa333e6f62f812eabf891a7e8f5f0b853a32593c1
checksum: 10/a5f85dfe76e27286c28a01d137769726ce3f758bcc03aa6b6f9e18700a40a08f57239f82e07efcab763c4b03a02d425edcc29fbecf40aad0124286978c6bc63c
languageName: node
linkType: hard
@@ -7822,23 +7807,6 @@ __metadata:
languageName: node
linkType: hard
"execa@npm:^5.0.0":
version: 5.1.1
resolution: "execa@npm:5.1.1"
dependencies:
cross-spawn: "npm:^7.0.3"
get-stream: "npm:^6.0.0"
human-signals: "npm:^2.1.0"
is-stream: "npm:^2.0.0"
merge-stream: "npm:^2.0.0"
npm-run-path: "npm:^4.0.1"
onetime: "npm:^5.1.2"
signal-exit: "npm:^3.0.3"
strip-final-newline: "npm:^2.0.0"
checksum: 10/8ada91f2d70f7dff702c861c2c64f21dfdc1525628f3c0454fd6f02fce65f7b958616cbd2b99ca7fa4d474e461a3d363824e91b3eb881705231abbf387470597
languageName: node
linkType: hard
"execa@npm:~8.0.1":
version: 8.0.1
resolution: "execa@npm:8.0.1"
@@ -7879,7 +7847,7 @@ __metadata:
languageName: node
linkType: hard
"express@npm:^4.17.3":
"express@npm:^4.19.2":
version: 4.19.2
resolution: "express@npm:4.19.2"
dependencies:
@@ -8918,11 +8886,11 @@ __metadata:
"@babel/plugin-transform-runtime": "npm:7.25.4"
"@babel/preset-env": "npm:7.25.4"
"@babel/preset-typescript": "npm:7.24.7"
"@babel/runtime": "npm:7.25.4"
"@babel/runtime": "npm:7.25.6"
"@braintree/sanitize-url": "npm:7.1.0"
"@bundle-stats/plugin-webpack-filter": "npm:4.15.0"
"@codemirror/autocomplete": "npm:6.18.0"
"@codemirror/commands": "npm:6.6.0"
"@codemirror/commands": "npm:6.6.1"
"@codemirror/language": "npm:6.10.2"
"@codemirror/legacy-modes": "npm:6.4.1"
"@codemirror/search": "npm:6.5.6"
@@ -9013,8 +8981,8 @@ __metadata:
"@types/webspeechapi": "npm:0.0.29"
"@typescript-eslint/eslint-plugin": "npm:7.18.0"
"@typescript-eslint/parser": "npm:7.18.0"
"@vaadin/combo-box": "npm:24.4.6"
"@vaadin/vaadin-themable-mixin": "npm:24.4.6"
"@vaadin/combo-box": "npm:24.4.7"
"@vaadin/vaadin-themable-mixin": "npm:24.4.7"
"@vibrant/color": "npm:3.2.1-alpha.1"
"@vibrant/core": "npm:3.2.1-alpha.1"
"@vibrant/quantizer-mmcq": "npm:3.2.1-alpha.1"
@@ -9043,8 +9011,8 @@ __metadata:
eslint-config-airbnb-base: "npm:15.0.0"
eslint-config-airbnb-typescript: "npm:18.0.0"
eslint-config-prettier: "npm:9.1.0"
eslint-import-resolver-webpack: "npm:0.13.8"
eslint-plugin-import: "npm:2.29.1"
eslint-import-resolver-webpack: "npm:0.13.9"
eslint-plugin-import: "npm:2.30.0"
eslint-plugin-lit: "npm:1.14.0"
eslint-plugin-lit-a11y: "npm:4.1.4"
eslint-plugin-unused-imports: "npm:4.1.3"
@@ -9070,7 +9038,7 @@ __metadata:
jszip: "npm:3.10.1"
leaflet: "npm:1.9.4"
leaflet-draw: "npm:1.0.4"
lint-staged: "npm:15.2.9"
lint-staged: "npm:15.2.10"
lit: "npm:2.8.0"
lit-analyzer: "npm:2.0.3"
lodash.merge: "npm:4.6.2"
@@ -9078,7 +9046,7 @@ __metadata:
luxon: "npm:3.5.0"
magic-string: "npm:0.30.11"
map-stream: "npm:0.0.7"
marked: "npm:14.1.0"
marked: "npm:14.1.1"
memoize-one: "npm:6.0.0"
mocha: "npm:10.5.0"
node-vibrant: "npm:3.2.1-alpha.1"
@@ -9098,7 +9066,7 @@ __metadata:
rrule: "npm:2.8.1"
serve-handler: "npm:6.1.5"
sinon: "npm:18.0.0"
sortablejs: "npm:1.15.2"
sortablejs: "npm:1.15.3"
stacktrace-js: "npm:2.0.2"
superstruct: "npm:2.0.2"
systemjs: "npm:6.15.1"
@@ -9118,7 +9086,7 @@ __metadata:
vue2-daterange-picker: "npm:0.6.8"
webpack: "npm:5.94.0"
webpack-cli: "npm:5.1.4"
webpack-dev-server: "npm:5.0.4"
webpack-dev-server: "npm:5.1.0"
webpack-manifest-plugin: "npm:5.0.0"
webpack-stats-plugin: "npm:1.1.3"
webpackbar: "npm:6.0.1"
@@ -9321,13 +9289,6 @@ __metadata:
languageName: node
linkType: hard
"human-signals@npm:^2.1.0":
version: 2.1.0
resolution: "human-signals@npm:2.1.0"
checksum: 10/df59be9e0af479036798a881d1f136c4a29e0b518d4abb863afbd11bf30efa3eeb1d0425fc65942dcc05ab3bf40205ea436b0ff389f2cd20b75b8643d539bf86
languageName: node
linkType: hard
"human-signals@npm:^5.0.0":
version: 5.0.0
resolution: "human-signals@npm:5.0.0"
@@ -9636,7 +9597,7 @@ __metadata:
languageName: node
linkType: hard
"is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1":
"is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.15.1":
version: 2.15.1
resolution: "is-core-module@npm:2.15.1"
dependencies:
@@ -10497,9 +10458,9 @@ __metadata:
languageName: node
linkType: hard
"lint-staged@npm:15.2.9":
version: 15.2.9
resolution: "lint-staged@npm:15.2.9"
"lint-staged@npm:15.2.10":
version: 15.2.10
resolution: "lint-staged@npm:15.2.10"
dependencies:
chalk: "npm:~5.3.0"
commander: "npm:~12.1.0"
@@ -10507,13 +10468,13 @@ __metadata:
execa: "npm:~8.0.1"
lilconfig: "npm:~3.1.2"
listr2: "npm:~8.2.4"
micromatch: "npm:~4.0.7"
micromatch: "npm:~4.0.8"
pidtree: "npm:~0.6.0"
string-argv: "npm:~0.3.2"
yaml: "npm:~2.5.0"
bin:
lint-staged: bin/lint-staged.js
checksum: 10/2f7342ca3fc7e2a8a0cc3db79ca8d2ad0269b98b13220f3a6745a514aacf1f83487a23a550569081ea962f9a576af7df8d687a8330a9c3c2c27348d5a4d5440e
checksum: 10/ab6930cd633dbb5b6ec7c81fc06c65df41e9f80d93dd22e0d79c6e272cdfd8110a0fbdec60303d46a06b30bcd92261153630e2c937531b77ec5ae41e7e9d90d3
languageName: node
linkType: hard
@@ -10858,12 +10819,12 @@ __metadata:
languageName: node
linkType: hard
"marked@npm:14.1.0":
version: 14.1.0
resolution: "marked@npm:14.1.0"
"marked@npm:14.1.1":
version: 14.1.1
resolution: "marked@npm:14.1.1"
bin:
marked: bin/marked.js
checksum: 10/1a930dd87a3994cc4fcc72c4668c548429d0e6363b8f7660193c106fa1cadcde5c813cfc3fe4be42b9f18b3652ba73469fb6c718215b0dbf8ddb9de2d1f5ab38
checksum: 10/43851120b9292102b490999cdbd8148dc814992af17fde1eabca68efe841e56951c0d3b0f2fd11f9a2b746f48e16a1959f455e0694677df1091d06fc73b12fc3
languageName: node
linkType: hard
@@ -10937,7 +10898,7 @@ __metadata:
languageName: node
linkType: hard
"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:~4.0.7":
"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:~4.0.8":
version: 4.0.8
resolution: "micromatch@npm:4.0.8"
dependencies:
@@ -10995,13 +10956,6 @@ __metadata:
languageName: node
linkType: hard
"mimic-fn@npm:^2.1.0":
version: 2.1.0
resolution: "mimic-fn@npm:2.1.0"
checksum: 10/d2421a3444848ce7f84bd49115ddacff29c15745db73f54041edc906c14b131a38d05298dae3081667627a59b2eb1ca4b436ff2e1b80f69679522410418b478a
languageName: node
linkType: hard
"mimic-fn@npm:^4.0.0":
version: 4.0.0
resolution: "mimic-fn@npm:4.0.0"
@@ -11444,15 +11398,6 @@ __metadata:
languageName: node
linkType: hard
"npm-run-path@npm:^4.0.1":
version: 4.0.1
resolution: "npm-run-path@npm:4.0.1"
dependencies:
path-key: "npm:^3.0.0"
checksum: 10/5374c0cea4b0bbfdfae62da7bbdf1e1558d338335f4cacf2515c282ff358ff27b2ecb91ffa5330a8b14390ac66a1e146e10700440c1ab868208430f56b5f4d23
languageName: node
linkType: hard
"npm-run-path@npm:^5.1.0":
version: 5.3.0
resolution: "npm-run-path@npm:5.3.0"
@@ -11518,7 +11463,7 @@ __metadata:
languageName: node
linkType: hard
"object.fromentries@npm:^2.0.7":
"object.fromentries@npm:^2.0.8":
version: 2.0.8
resolution: "object.fromentries@npm:2.0.8"
dependencies:
@@ -11530,7 +11475,7 @@ __metadata:
languageName: node
linkType: hard
"object.groupby@npm:^1.0.1":
"object.groupby@npm:^1.0.3":
version: 1.0.3
resolution: "object.groupby@npm:1.0.3"
dependencies:
@@ -11550,7 +11495,7 @@ __metadata:
languageName: node
linkType: hard
"object.values@npm:^1.1.7":
"object.values@npm:^1.2.0":
version: 1.2.0
resolution: "object.values@npm:1.2.0"
dependencies:
@@ -11600,15 +11545,6 @@ __metadata:
languageName: node
linkType: hard
"onetime@npm:^5.1.2":
version: 5.1.2
resolution: "onetime@npm:5.1.2"
dependencies:
mimic-fn: "npm:^2.1.0"
checksum: 10/e9fd0695a01cf226652f0385bf16b7a24153dbbb2039f764c8ba6d2306a8506b0e4ce570de6ad99c7a6eb49520743afdb66edd95ee979c1a342554ed49a9aadd
languageName: node
linkType: hard
"onetime@npm:^6.0.0":
version: 6.0.0
resolution: "onetime@npm:6.0.0"
@@ -11934,7 +11870,7 @@ __metadata:
languageName: node
linkType: hard
"path-key@npm:^3.0.0, path-key@npm:^3.1.0":
"path-key@npm:^3.1.0":
version: 3.1.1
resolution: "path-key@npm:3.1.1"
checksum: 10/55cd7a9dd4b343412a8386a743f9c746ef196e57c823d90ca3ab917f90ab9f13dd0ded27252ba49dbdfcab2b091d998bc446f6220cd3cea65db407502a740020
@@ -13163,13 +13099,6 @@ __metadata:
languageName: node
linkType: hard
"signal-exit@npm:^3.0.3":
version: 3.0.7
resolution: "signal-exit@npm:3.0.7"
checksum: 10/a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318
languageName: node
linkType: hard
"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0":
version: 4.1.0
resolution: "signal-exit@npm:4.1.0"
@@ -13271,17 +13200,17 @@ __metadata:
languageName: node
linkType: hard
"sortablejs@npm:1.15.2":
version: 1.15.2
resolution: "sortablejs@npm:1.15.2"
checksum: 10/d149dd04bb05904ea20ca477d97cdfbbbd46edf7ede8c80958f2ad881dd6d7b1633cce31665992b341e0ea01fc7ef9d7571ccb7eb995baf01f9418b44e935eac
"sortablejs@npm:1.15.3":
version: 1.15.3
resolution: "sortablejs@npm:1.15.3"
checksum: 10/85d39a172ef47adedf273afa65daa8aefcbaafd43a5b5c480d8637add93033f5784da697d0d3545d9bb6e11fd71f1847f307ee26be452942f3785a683fd44bb5
languageName: node
linkType: hard
"sortablejs@patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch":
version: 1.15.2
resolution: "sortablejs@patch:sortablejs@npm%3A1.15.2#~/.yarn/patches/sortablejs-npm-1.15.2-73347ae85a.patch::version=1.15.2&hash=1591ab"
checksum: 10/d44399e9ca660157c76b13705eaa26191f71c4bd025e2d47b9f7e50a8f9bdb7deaaa2783a8032e55f39627fa4007042bcfd62cb4bbeb2931f6a5d6ee06047e2e
"sortablejs@patch:sortablejs@npm%3A1.15.3#~/.yarn/patches/sortablejs-npm-1.15.3-3235a8f83b.patch":
version: 1.15.3
resolution: "sortablejs@patch:sortablejs@npm%3A1.15.3#~/.yarn/patches/sortablejs-npm-1.15.3-3235a8f83b.patch::version=1.15.3&hash=fba0ad"
checksum: 10/249f4cfd2b4a811f4e1505b25d4d67a97521afabdabd2f5482f985da20785d995c2899d442ad79075c7ba547e477aef81f09b0028f12d1de468cbca4f2b8c043
languageName: node
linkType: hard
@@ -13649,13 +13578,6 @@ __metadata:
languageName: node
linkType: hard
"strip-final-newline@npm:^2.0.0":
version: 2.0.0
resolution: "strip-final-newline@npm:2.0.0"
checksum: 10/69412b5e25731e1938184b5d489c32e340605bb611d6140344abc3421b7f3c6f9984b21dff296dfcf056681b82caa3bb4cc996a965ce37bcfad663e92eae9c64
languageName: node
linkType: hard
"strip-final-newline@npm:^3.0.0":
version: 3.0.0
resolution: "strip-final-newline@npm:3.0.0"
@@ -14861,7 +14783,7 @@ __metadata:
languageName: node
linkType: hard
"webpack-dev-middleware@npm:^7.1.0":
"webpack-dev-middleware@npm:^7.4.2":
version: 7.4.2
resolution: "webpack-dev-middleware@npm:7.4.2"
dependencies:
@@ -14880,9 +14802,9 @@ __metadata:
languageName: node
linkType: hard
"webpack-dev-server@npm:5.0.4":
version: 5.0.4
resolution: "webpack-dev-server@npm:5.0.4"
"webpack-dev-server@npm:5.1.0":
version: 5.1.0
resolution: "webpack-dev-server@npm:5.1.0"
dependencies:
"@types/bonjour": "npm:^3.5.13"
"@types/connect-history-api-fallback": "npm:^1.5.4"
@@ -14897,8 +14819,7 @@ __metadata:
colorette: "npm:^2.0.10"
compression: "npm:^1.7.4"
connect-history-api-fallback: "npm:^2.0.0"
default-gateway: "npm:^6.0.3"
express: "npm:^4.17.3"
express: "npm:^4.19.2"
graceful-fs: "npm:^4.2.6"
html-entities: "npm:^2.4.0"
http-proxy-middleware: "npm:^2.0.3"
@@ -14906,14 +14827,13 @@ __metadata:
launch-editor: "npm:^2.6.1"
open: "npm:^10.0.3"
p-retry: "npm:^6.2.0"
rimraf: "npm:^5.0.5"
schema-utils: "npm:^4.2.0"
selfsigned: "npm:^2.4.1"
serve-index: "npm:^1.9.1"
sockjs: "npm:^0.3.24"
spdy: "npm:^4.0.2"
webpack-dev-middleware: "npm:^7.1.0"
ws: "npm:^8.16.0"
webpack-dev-middleware: "npm:^7.4.2"
ws: "npm:^8.18.0"
peerDependencies:
webpack: ^5.0.0
peerDependenciesMeta:
@@ -14923,7 +14843,7 @@ __metadata:
optional: true
bin:
webpack-dev-server: bin/webpack-dev-server.js
checksum: 10/3896866abf15a1d5cc31ab4fc9c36aacf3431356837ad6debe25cde29289a70c58dcbe40914bbb275ff455463d37437532093d0e8d7744e7643ce1364491fdb4
checksum: 10/f23255681cc5e2c2709b23ca7b2185aeed83b1c9912657d4512eda8685625a46d7a103a92446494a55fe2afdfab936f9bd4f037d20b52f7fdfff303e7e7199c7
languageName: node
linkType: hard
@@ -15476,7 +15396,7 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:^8.16.0":
"ws@npm:^8.18.0":
version: 8.18.0
resolution: "ws@npm:8.18.0"
peerDependencies: