diff --git a/.github/workflows/relative-ci.yaml b/.github/workflows/relative-ci.yaml index 0bda279c9a5f..e6dc81a49ccd 100644 --- a/.github/workflows/relative-ci.yaml +++ b/.github/workflows/relative-ci.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Send bundle stats and build information to RelativeCI - uses: relative-ci/agent-action@v2.1.10 + uses: relative-ci/agent-action@v2.1.11 with: key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }} token: ${{ github.token }} diff --git a/demo/src/stubs/lovelace.ts b/demo/src/stubs/lovelace.ts index 6f831c325d91..53ada3e2e07f 100644 --- a/demo/src/stubs/lovelace.ts +++ b/demo/src/stubs/lovelace.ts @@ -19,15 +19,15 @@ export const mockLovelace = ( hass.mockWS("lovelace/resources", () => Promise.resolve([])); }; -customElements.whenDefined("hui-view").then(() => { +customElements.whenDefined("hui-card").then(() => { // eslint-disable-next-line - const HUIView = customElements.get("hui-view"); + const HUIView = customElements.get("hui-card"); // Patch HUI-VIEW to make the lovelace object available to the demo card - const oldCreateCard = HUIView!.prototype.createCardElement; + const oldCreateCard = HUIView!.prototype.createElement; - HUIView!.prototype.createCardElement = function (config) { + HUIView!.prototype.createElement = function (config) { const el = oldCreateCard.call(this, config); - if (el.tagName === "HA-DEMO-CARD") { + if (config.type === "custom:ha-demo-card") { (el as HADemoCard).lovelace = this.lovelace; } return el; diff --git a/package.json b/package.json index f85a53aaaf4c..cb9175955eea 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "dependencies": { "@babel/runtime": "7.24.6", "@braintree/sanitize-url": "7.0.2", - "@codemirror/autocomplete": "6.16.0", + "@codemirror/autocomplete": "6.16.2", "@codemirror/commands": "6.5.0", "@codemirror/language": "6.10.1", "@codemirror/legacy-modes": "6.4.0", @@ -133,7 +133,7 @@ "tinykeys": "2.1.0", "tsparticles-engine": "2.12.0", "tsparticles-preset-links": "2.12.0", - "ua-parser-js": "1.0.37", + "ua-parser-js": "1.0.38", "unfetch": "5.0.0", "vis-data": "7.1.9", "vis-network": "9.1.9", @@ -185,8 +185,8 @@ "@types/tar": "6.1.13", "@types/ua-parser-js": "0.7.39", "@types/webspeechapi": "0.0.29", - "@typescript-eslint/eslint-plugin": "7.10.0", - "@typescript-eslint/parser": "7.10.0", + "@typescript-eslint/eslint-plugin": "7.11.0", + "@typescript-eslint/parser": "7.11.0", "@web/dev-server": "0.1.38", "@web/dev-server-rollup": "0.4.1", "babel-loader": "9.1.3", @@ -199,7 +199,7 @@ "eslint-config-prettier": "9.1.0", "eslint-import-resolver-webpack": "0.13.8", "eslint-plugin-import": "2.29.1", - "eslint-plugin-lit": "1.13.0", + "eslint-plugin-lit": "1.14.0", "eslint-plugin-lit-a11y": "4.1.2", "eslint-plugin-unused-imports": "4.0.0", "eslint-plugin-wc": "2.1.0", @@ -233,7 +233,7 @@ "sinon": "18.0.0", "source-map-url": "0.4.1", "systemjs": "6.15.1", - "tar": "7.1.0", + "tar": "7.2.0", "terser-webpack-plugin": "5.3.10", "transform-async-modules-webpack-plugin": "1.1.1", "ts-lit-plugin": "2.0.2", @@ -244,7 +244,7 @@ "webpack-manifest-plugin": "5.0.0", "webpack-stats-plugin": "1.1.3", "webpackbar": "6.0.1", - "workbox-build": "7.1.0" + "workbox-build": "7.1.1" }, "_comment": "Polymer 3.2 contained a bug, fixed in https://github.com/Polymer/polymer/pull/5569, add as patch", "resolutions": { diff --git a/pyproject.toml b/pyproject.toml index 32bf3ddd7cf6..9448a16a3f34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "home-assistant-frontend" -version = "20240530.0" +version = "20240603.0" license = {text = "Apache-2.0"} description = "The Home Assistant frontend" readme = "README.md" diff --git a/src/components/ha-icon-picker.ts b/src/components/ha-icon-picker.ts index 293ee5cd11a4..df792a73bb73 100644 --- a/src/components/ha-icon-picker.ts +++ b/src/components/ha-icon-picker.ts @@ -198,8 +198,7 @@ export class HaIconPicker extends LitElement { static get styles() { return css` - ha-icon, - ha-svg-icon { + *[slot="icon"] { color: var(--primary-text-color); position: relative; bottom: 2px; diff --git a/src/components/map/ha-entity-marker.ts b/src/components/map/ha-entity-marker.ts index 248ace4af741..178de1063f6c 100644 --- a/src/components/map/ha-entity-marker.ts +++ b/src/components/map/ha-entity-marker.ts @@ -48,7 +48,7 @@ class HaEntityMarker extends LitElement { width: 48px; height: 48px; font-size: var(--ha-marker-font-size, 1.5em); - border-radius: 50%; + border-radius: var(--ha-marker-border-radius, 50%); border: 1px solid var(--ha-marker-color, var(--primary-color)); color: var(--primary-text-color); background-color: var(--card-background-color); diff --git a/src/data/recorder.ts b/src/data/recorder.ts index e43a221e9588..23b63a6d1cb4 100644 --- a/src/data/recorder.ts +++ b/src/data/recorder.ts @@ -12,14 +12,6 @@ export interface RecorderInfo { thread_running: boolean; } -export interface RecordedEntities { - entity_ids: string[]; -} -export interface RecordedExcludedEntities { - recorded_ids: string[]; - excluded_ids: string[]; -} - export type StatisticType = "change" | "state" | "sum" | "min" | "max" | "mean"; export interface Statistics { @@ -332,25 +324,3 @@ export const getDisplayUnit = ( export const isExternalStatistic = (statisticsId: string): boolean => statisticsId.includes(":"); - -let recordedExcludedEntitiesCache: RecordedExcludedEntities | undefined; - -export const getRecordedExcludedEntities = async ( - hass: HomeAssistant -): Promise => { - if (recordedExcludedEntitiesCache) { - return recordedExcludedEntitiesCache; - } - const recordedEntities = await hass.callWS({ - type: "recorder/recorded_entities", - }); - - recordedExcludedEntitiesCache = { - recorded_ids: recordedEntities.entity_ids, - excluded_ids: Object.keys(hass.states).filter( - (id) => !recordedEntities.entity_ids.includes(id) - ), - }; - - return recordedExcludedEntitiesCache; -}; diff --git a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts index b668c2c9ff46..c1fab545eda8 100644 --- a/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts +++ b/src/dialogs/voice-command-dialog/ha-voice-command-dialog.ts @@ -756,6 +756,7 @@ export class HaVoiceCommandDialog extends LitElement { max-height: 100%; } .message { + white-space: pre-line; font-size: 18px; clear: both; margin: 8px 0; @@ -792,10 +793,14 @@ export class HaVoiceCommandDialog extends LitElement { direction: var(--direction); } - .message a { + .message.user a { color: var(--text-primary-color); } + .message.hass a { + color: var(--primary-text-color); + } + .message img { width: 100%; border-radius: 10px; diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index 40f7185592e9..0418f9b7010f 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -145,6 +145,14 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { @state() private _filteredAutomations?: string[] | null; + @storage({ + storage: "sessionStorage", + key: "automation-table-search", + state: true, + subscribe: false, + }) + private _filter = ""; + @storage({ storage: "sessionStorage", key: "automation-table-filters-full", @@ -547,6 +555,8 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { "ui.panel.config.automation.picker.no_automations" )} @clear-filter=${this._clearFilter} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} hasFab clickable class=${this.narrow ? "narrow" : ""} @@ -924,6 +934,10 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { this._applyFilters(); }; + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value; + } + private _filterChanged(ev) { const type = ev.target.localName; this._filters = { ...this._filters, [type]: ev.detail }; diff --git a/src/panels/config/blueprint/ha-blueprint-overview.ts b/src/panels/config/blueprint/ha-blueprint-overview.ts index 45f02be911ee..d53f31411cbc 100644 --- a/src/panels/config/blueprint/ha-blueprint-overview.ts +++ b/src/panels/config/blueprint/ha-blueprint-overview.ts @@ -107,6 +107,14 @@ class HaBlueprintOverview extends LitElement { }) private _activeCollapsed?: string; + @storage({ + storage: "sessionStorage", + key: "blueprint-table-search", + state: true, + subscribe: false, + }) + private _filter: string = ""; + private _processedBlueprints = memoizeOne( ( blueprints: Record, @@ -308,6 +316,8 @@ class HaBlueprintOverview extends LitElement { @sorting-changed=${this._handleSortingChanged} @grouping-changed=${this._handleGroupingChanged} @collapsed-changed=${this._handleCollapseChanged} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} > { this._entitySources = sources; }); } private _setFiltersFromUrl() { - if (this._searchParms.has("domain")) { - this._filters = { - ...this._filters, - "ha-filter-states": { - value: [], - items: undefined, - }, - "ha-filter-integrations": { - value: [this._searchParms.get("domain")!], - items: undefined, - }, - }; - } - if (this._searchParms.has("config_entry")) { - this._filters = { - ...this._filters, - "ha-filter-states": { - value: [], - items: undefined, - }, - config_entry: { - value: [this._searchParms.get("config_entry")!], - items: undefined, - }, - }; + const domain = this._searchParms.get("domain"); + const configEntry = this._searchParms.get("config_entry"); + + if (!domain && !configEntry) { + return; } + + this._filter = history.state?.filter || ""; + + this._filters = { + "ha-filter-states": { + value: [], + items: undefined, + }, + "ha-filter-integrations": { + value: domain ? [domain] : [], + items: undefined, + }, + config_entry: { + value: configEntry ? [configEntry] : [], + items: undefined, + }, + }; + if (this._searchParms.has("label")) { this._filterLabel(); } diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index a080b9041d49..032eb362f6d4 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -159,6 +159,14 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { }) private _activeCollapsed?: string; + @storage({ + storage: "sessionStorage", + key: "helpers-table-search", + state: true, + subscribe: false, + }) + private _filter = ""; + @state() private _stateItems: HassEntity[] = []; @state() private _entityEntries?: Record; @@ -559,6 +567,8 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { .activeFilters=${this._activeFilters} @clear-filter=${this._clearFilter} @row-click=${this._openEditDialog} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} hasFab clickable .noDataText=${this.hass.localize( @@ -1070,6 +1080,10 @@ ${rejected this._activeCollapsed = ev.detail.value; } + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value; + } + static get styles(): CSSResultGroup { return [ haStyle, diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 8ef1ddebb953..7a729dbff17e 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -453,7 +453,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { const attention = ATTENTION_SOURCES.includes( flow.context.source ); - return html` ${flow.localized_title} @@ -477,8 +477,15 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { > `; })} - ${attentionEntries.map((item) => - this._renderConfigEntry(item) + ${attentionEntries.map( + (item, index) => + html`${this._renderConfigEntry(item)} + ${index < attentionEntries.length - 1 + ? html` ` + : ""} ` )} ` @@ -502,7 +509,16 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ` : nothing} - ${normalEntries.map((item) => this._renderConfigEntry(item))} + ${normalEntries.map( + (item, index) => + html`${this._renderConfigEntry(item)} + ${index < normalEntries.length - 1 + ? html` ` + : ""} ` + )}
@@ -667,19 +683,21 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { const configPanel = this._configPanel(item.domain, this.hass.panels); - return html` - ${item.title || domainToName(this.hass.localize, item.domain)} +
+ ${item.title || domainToName(this.hass.localize, item.domain)} +
${devicesLine}
${stateText @@ -726,67 +744,62 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { .path=${mdiDotsVertical} > ${item.supports_options && stateText - ? html` - + ? html` + ${this.hass.localize( "ui.panel.config.integrations.config_entry.configure" )} - ` + ` : ""} ${item.disabled_by && devices.length - ? html` - - + ? html` + + ${this.hass.localize( `ui.panel.config.integrations.config_entry.devices`, { count: devices.length } )} - - - ` + + + ` : ""} ${item.disabled_by && services.length - ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.services`, - { count: services.length } - )} - - - ` + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.services`, + { count: services.length } + )} + + ` : ""} ${item.disabled_by && entities.length - ? html` - + ? html` + ${this.hass.localize( `ui.panel.config.integrations.config_entry.entities`, { count: entities.length } )} - - - ` + + + ` : ""} ${!item.disabled_by && RECOVERABLE_STATES.includes(item.state) && @@ -814,7 +827,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ${this._diagnosticHandler && item.state === "loaded" ? html` @@ -1402,18 +1415,8 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { margin-top: 16px; } ha-list-item-new.discovered { - --mdc-list-item-meta-size: auto; - --mdc-list-item-meta-display: flex; height: 72px; } - ha-list-item-new.config_entry { - overflow: visible; - --mdc-list-item-meta-size: auto; - --mdc-list-item-meta-display: flex; - } - ha-button-menu-new ha-menu-item { - --mdc-icon-size: 24px; - } ha-list-item-new.config_entry::after { position: absolute; top: 8px; @@ -1424,9 +1427,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { pointer-events: none; content: ""; } - ha-button-menu-new { - flex: 0; - } a { text-decoration: none; } @@ -1483,6 +1483,10 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { overflow: hidden; text-overflow: ellipsis; } + .state-disabled [slot="headline"], + .state-disabled [slot="supporting-text"] { + opacity: var(--md-list-item-disabled-opacity, 0.3); + } ha-list-new { margin-top: 8px; margin-bottom: 8px; diff --git a/src/panels/config/labels/ha-config-labels.ts b/src/panels/config/labels/ha-config-labels.ts index 43f346ce6a71..4164f33e3af1 100644 --- a/src/panels/config/labels/ha-config-labels.ts +++ b/src/panels/config/labels/ha-config-labels.ts @@ -51,6 +51,14 @@ export class HaConfigLabels extends LitElement { @state() private _labels: LabelRegistryEntry[] = []; + @storage({ + storage: "sessionStorage", + key: "labels-table-search", + state: true, + subscribe: false, + }) + private _filter = ""; + @storage({ key: "labels-table-sort", state: false, @@ -160,6 +168,8 @@ export class HaConfigLabels extends LitElement { hasFab .initialSorting=${this._activeSorting} @sorting-changed=${this._handleSortingChanged} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} @row-click=${this._editLabel} clickable id="label_id" @@ -283,6 +293,10 @@ export class HaConfigLabels extends LitElement { private _handleSortingChanged(ev: CustomEvent) { this._activeSorting = ev.detail; } + + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value; + } } declare global { diff --git a/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts b/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts index fcdf220845c0..15df18b00230 100644 --- a/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts +++ b/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts @@ -70,6 +70,14 @@ export class HaConfigLovelaceDashboards extends LitElement { @state() private _dashboards: LovelaceDashboard[] = []; + @storage({ + storage: "sessionStorage", + key: "lovelace-dashboards-table-search", + state: true, + subscribe: false, + }) + private _filter: string = ""; + @storage({ key: "lovelace-dashboards-table-sort", state: false, @@ -304,6 +312,8 @@ export class HaConfigLovelaceDashboards extends LitElement { .data=${this._getItems(this._dashboards)} .initialSorting=${this._activeSorting} @sorting-changed=${this._handleSortingChanged} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} @row-click=${this._editDashboard} id="url_path" hasFab @@ -453,6 +463,10 @@ export class HaConfigLovelaceDashboards extends LitElement { private _handleSortingChanged(ev: CustomEvent) { this._activeSorting = ev.detail; } + + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value; + } } declare global { diff --git a/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts b/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts index 3131af913ee5..ab87058e6944 100644 --- a/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts +++ b/src/panels/config/lovelace/resources/ha-config-lovelace-resources.ts @@ -52,6 +52,14 @@ export class HaConfigLovelaceRescources extends LitElement { @state() private _resources: LovelaceResource[] = []; + @storage({ + storage: "sessionStorage", + key: "lovelace-resources-table-search", + state: true, + subscribe: false, + }) + private _filter = ""; + @storage({ key: "lovelace-resources-table-sort", state: false, @@ -138,6 +146,8 @@ export class HaConfigLovelaceRescources extends LitElement { )} .initialSorting=${this._activeSorting} @sorting-changed=${this._handleSortingChanged} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} @row-click=${this._editResource} hasFab clickable @@ -252,6 +262,10 @@ export class HaConfigLovelaceRescources extends LitElement { this._activeSorting = ev.detail; } + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value; + } + static get styles(): CSSResultGroup { return [ haStyle, diff --git a/src/panels/config/scene/ha-scene-dashboard.ts b/src/panels/config/scene/ha-scene-dashboard.ts index e479c89a11ad..4f2fca1c9690 100644 --- a/src/panels/config/scene/ha-scene-dashboard.ts +++ b/src/panels/config/scene/ha-scene-dashboard.ts @@ -137,6 +137,14 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { @state() private _filteredScenes?: string[] | null; + @storage({ + storage: "sessionStorage", + key: "scene-table-search", + state: true, + subscribe: false, + }) + private _filter = ""; + @storage({ storage: "sessionStorage", key: "scene-table-filters-full", @@ -543,6 +551,8 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { "ui.panel.config.scene.picker.no_scenes" )} @clear-filter=${this._clearFilter} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} hasFab clickable @row-click=${this._handleRowClicked} @@ -1141,6 +1151,10 @@ ${rejected this._activeCollapsed = ev.detail.value; } + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value; + } + static get styles(): CSSResultGroup { return [ haStyle, diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index 551d5030f4ec..c5b19b163033 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -141,6 +141,14 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { @state() private _filteredScripts?: string[] | null; + @storage({ + storage: "sessionStorage", + key: "script-table-search", + state: true, + subscribe: false, + }) + private _filter = ""; + @storage({ storage: "sessionStorage", key: "script-table-filters-full", @@ -558,6 +566,8 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { "ui.panel.config.script.picker.no_scripts" )} @clear-filter=${this._clearFilter} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} hasFab clickable class=${this.narrow ? "narrow" : ""} @@ -800,6 +810,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { `; } + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value; + } + private _filterExpanded(ev) { if (ev.detail.expanded) { this._expandedFilter = ev.target.localName; diff --git a/src/panels/config/tags/ha-config-tags.ts b/src/panels/config/tags/ha-config-tags.ts index 24f104952b2e..90c69f924bd1 100644 --- a/src/panels/config/tags/ha-config-tags.ts +++ b/src/panels/config/tags/ha-config-tags.ts @@ -35,6 +35,7 @@ import { documentationUrl } from "../../../util/documentation-url"; import { configSections } from "../ha-panel-config"; import { showTagDetailDialog } from "./show-dialog-tag-detail"; import "./tag-image"; +import { storage } from "../../../common/decorators/storage"; export interface TagRowData extends Tag { display_name: string; @@ -57,6 +58,14 @@ export class HaConfigTags extends SubscribeMixin(LitElement) { return this.hass.auth.external?.config.canWriteTag; } + @storage({ + storage: "sessionStorage", + key: "tags-table-search", + state: true, + subscribe: false, + }) + private _filter = ""; + private _columns = memoizeOne( (narrow: boolean, _language, localize: LocalizeFunc) => { const columns: DataTableColumnContainer = { @@ -189,6 +198,8 @@ export class HaConfigTags extends SubscribeMixin(LitElement) { )} .data=${this._data(this._tags)} .noDataText=${this.hass.localize("ui.panel.config.tag.no_tags")} + .filter=${this._filter} + @search-changed=${this._handleSearchChange} hasFab > ; - @state() private _filter: string = history.state?.filter || ""; + @storage({ + storage: "sessionStorage", + key: "voice-expose-table-search", + state: true, + subscribe: false, + }) + private _filter = ""; @state() private _searchParms = new URLSearchParams(window.location.search); @@ -634,7 +640,6 @@ export class VoiceAssistantsExpose extends LitElement { private _handleSearchChange(ev: CustomEvent) { this._filter = ev.detail.value; - history.replaceState({ filter: this._filter }, ""); } private _handleSelectionChanged( diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts index eef935aa4d97..1f9f60073e53 100644 --- a/src/panels/history/ha-panel-history.ts +++ b/src/panels/history/ha-panel-history.ts @@ -6,7 +6,6 @@ import { } from "@mdi/js"; import { ActionDetail } from "@material/mwc-list"; import { differenceInHours } from "date-fns"; -import { HassEntity } from "home-assistant-js-websocket"; import { HassServiceTarget, UnsubscribeFunc, @@ -46,11 +45,7 @@ import { computeHistory, subscribeHistory, } from "../../data/history"; -import { - fetchStatistics, - Statistics, - getRecordedExcludedEntities, -} from "../../data/recorder"; +import { Statistics, fetchStatistics } from "../../data/recorder"; import { expandAreaTarget, expandDeviceTarget, @@ -100,8 +95,6 @@ class HaPanelHistory extends LitElement { private _interval?: number; - private _excludedEntities?: string[]; - public constructor() { super(); @@ -192,7 +185,6 @@ class HaPanelHistory extends LitElement { .hass=${this.hass} .value=${this._targetPickerValue} .disabled=${this._isLoading} - .entityFilter=${this._entityFilter} addOnTop @value-changed=${this._targetsChanged} > @@ -219,10 +211,6 @@ class HaPanelHistory extends LitElement { `; } - private _entityFilter = (entity: HassEntity): boolean => - !this._excludedEntities || - !this._excludedEntities.includes(entity.entity_id); - private mergeHistoryResults( ltsResult: HistoryResult, historyResult: HistoryResult @@ -374,17 +362,8 @@ class HaPanelHistory extends LitElement { } } - private async _getRecordedExcludedEntities() { - const { recorded_ids: _recordedIds, excluded_ids: excludedIds } = - await getRecordedExcludedEntities(this.hass); - this._excludedEntities = excludedIds; - } - protected firstUpdated(changedProps: PropertyValues) { super.firstUpdated(changedProps); - - this._getRecordedExcludedEntities(); - const searchParams = extractSearchParamsObject(); if (searchParams.back === "1" && history.length > 1) { this._showBack = true; diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index 55809e58d04d..7d931632c269 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -12,11 +12,19 @@ import { import { createCardElement } from "../create-element/create-card-element"; import type { Lovelace, LovelaceCard, LovelaceLayoutOptions } from "../types"; +declare global { + interface HASSDomEvents { + "card-visibility-changed": { value: boolean }; + } +} + @customElement("hui-card") export class HuiCard extends ReactiveElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public lovelace!: Lovelace; + @property({ attribute: false }) public lovelace?: Lovelace; + + @property({ attribute: false }) public isPanel = false; @state() public _config?: LovelaceCardConfig; @@ -59,14 +67,25 @@ export class HuiCard extends ReactiveElement { return configOptions; } + // Public to make demo happy + public createElement(config: LovelaceCardConfig) { + const element = createCardElement(config) as LovelaceCard; + element.hass = this.hass; + element.editMode = this.lovelace?.editMode; + // Update element when the visibility of the card changes (e.g. conditional card or filter card) + element.addEventListener("card-visibility-changed", (ev) => { + ev.stopPropagation(); + this._updateElement(); + }); + return element; + } + public setConfig(config: LovelaceCardConfig): void { if (this._config === config) { return; } this._config = config; - this._element = createCardElement(config); - this._element.hass = this.hass; - this._element.editMode = this.lovelace.editMode; + this._element = this.createElement(config); while (this.lastChild) { this.removeChild(this.lastChild); @@ -82,11 +101,14 @@ export class HuiCard extends ReactiveElement { this._element.hass = this.hass; } if (changedProperties.has("lovelace")) { - this._element.editMode = this.lovelace.editMode; + this._element.editMode = this.lovelace?.editMode; } if (changedProperties.has("hass") || changedProperties.has("lovelace")) { this._updateElement(); } + if (changedProperties.has("isPanel")) { + this._element.isPanel = this.isPanel; + } } } @@ -118,9 +140,16 @@ export class HuiCard extends ReactiveElement { if (!this._element) { return; } + + if (this._element.hidden) { + this.style.setProperty("display", "none"); + this.toggleAttribute("hidden", true); + return; + } + const visible = forceVisible || - this.lovelace.editMode || + this.lovelace?.editMode || !this._config?.visibility || checkConditionsMet(this._config.visibility, this.hass); diff --git a/src/panels/lovelace/cards/hui-conditional-card.ts b/src/panels/lovelace/cards/hui-conditional-card.ts index 96d3120eaddb..b39d24e7bf3e 100644 --- a/src/panels/lovelace/cards/hui-conditional-card.ts +++ b/src/panels/lovelace/cards/hui-conditional-card.ts @@ -1,4 +1,5 @@ import { customElement } from "lit/decorators"; +import { fireEvent } from "../../../common/dom/fire_event"; import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; import { computeCardSize } from "../common/compute-card-size"; import { HuiConditionalBase } from "../components/hui-conditional-base"; @@ -58,6 +59,15 @@ class HuiConditionalCard extends HuiConditionalBase implements LovelaceCard { this.replaceChild(this._element, this.lastChild); } } + + protected setVisibility(conditionMet: boolean): void { + const visible = this.editMode || conditionMet; + const previouslyHidden = this.hidden; + super.setVisibility(conditionMet); + if (previouslyHidden !== this.hidden) { + fireEvent(this, "card-visibility-changed", { value: visible }); + } + } } declare global { diff --git a/src/panels/lovelace/cards/hui-entity-filter-card.ts b/src/panels/lovelace/cards/hui-entity-filter-card.ts index ab6fb943b265..efa3d9730ac1 100644 --- a/src/panels/lovelace/cards/hui-entity-filter-card.ts +++ b/src/panels/lovelace/cards/hui-entity-filter-card.ts @@ -15,6 +15,7 @@ import { createCardElement } from "../create-element/create-card-element"; import { EntityFilterEntityConfig } from "../entity-rows/types"; import { LovelaceCard } from "../types"; import { EntityFilterCardConfig } from "./types"; +import { fireEvent } from "../../../common/dom/fire_event"; @customElement("hui-entity-filter-card") export class HuiEntityFilterCard @@ -162,9 +163,14 @@ export class HuiEntityFilterCard return false; }); - if (entitiesList.length === 0 && this._config.show_empty === false) { + if ( + entitiesList.length === 0 && + this._config.show_empty === false && + !this.hidden + ) { this.style.display = "none"; this.toggleAttribute("hidden", true); + fireEvent(this, "card-visibility-changed", { value: false }); return; } @@ -194,8 +200,11 @@ export class HuiEntityFilterCard this.appendChild(this._element); } - this.style.display = "block"; - this.toggleAttribute("hidden", false); + if (this.hidden) { + this.style.display = "block"; + this.toggleAttribute("hidden", false); + fireEvent(this, "card-visibility-changed", { value: true }); + } } private _haveEntitiesChanged(oldHass: HomeAssistant | null): boolean { diff --git a/src/panels/lovelace/cards/hui-todo-list-card.ts b/src/panels/lovelace/cards/hui-todo-list-card.ts index 23f2c1ff362d..5f2d3227c1b1 100644 --- a/src/panels/lovelace/cards/hui-todo-list-card.ts +++ b/src/panels/lovelace/cards/hui-todo-list-card.ts @@ -104,6 +104,7 @@ export class HuiTodoListCard extends LitElement implements LovelaceCard { disconnectedCallback(): void { super.disconnectedCallback(); this._unsubItems?.then((unsub) => unsub()); + this._unsubItems = undefined; } public getCardSize(): number { diff --git a/src/panels/lovelace/components/hui-conditional-base.ts b/src/panels/lovelace/components/hui-conditional-base.ts index 764481047d78..f392fc5c806d 100644 --- a/src/panels/lovelace/components/hui-conditional-base.ts +++ b/src/panels/lovelace/components/hui-conditional-base.ts @@ -6,14 +6,20 @@ import { HomeAssistant } from "../../../types"; import { ConditionalCardConfig } from "../cards/types"; import { Condition, - checkConditionsMet, attachConditionMediaQueriesListeners, + checkConditionsMet, extractMediaQueries, validateConditionalConfig, } from "../common/validate-condition"; import { ConditionalRowConfig, LovelaceRow } from "../entity-rows/types"; import { LovelaceCard } from "../types"; +declare global { + interface HASSDomEvents { + "visibility-changed": { value: boolean }; + } +} + @customElement("hui-conditional-base") export class HuiConditionalBase extends ReactiveElement { @property({ attribute: false }) public hass?: HomeAssistant; @@ -95,7 +101,7 @@ export class HuiConditionalBase extends ReactiveElement { supportedConditions, (matches) => { if (hasOnlyMediaQuery) { - this._setVisibility(matches); + this.setVisibility(matches); return; } this._updateVisibility(); @@ -129,17 +135,18 @@ export class HuiConditionalBase extends ReactiveElement { this.hass! ); - this._setVisibility(conditionMet); + this.setVisibility(conditionMet); } - private _setVisibility(conditionMet: boolean) { + protected setVisibility(conditionMet: boolean) { if (!this._element || !this.hass) { return; } const visible = this.editMode || conditionMet; - this.toggleAttribute("hidden", !visible); - this.style.setProperty("display", visible ? "" : "none"); - + if (this.hidden !== !visible) { + this.toggleAttribute("hidden", !visible); + this.style.setProperty("display", visible ? "" : "none"); + } if (visible) { this._element.hass = this.hass; if (!this._element!.parentElement) { diff --git a/src/panels/lovelace/editor/card-editor/hui-card-element-editor.ts b/src/panels/lovelace/editor/card-editor/hui-card-element-editor.ts index 7c17bddea736..6300c50e11e9 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-element-editor.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-element-editor.ts @@ -1,11 +1,22 @@ -import { customElement } from "lit/decorators"; +import "@polymer/paper-tabs/paper-tab"; +import "@polymer/paper-tabs/paper-tabs"; +import { CSSResultGroup, TemplateResult, css, html, nothing } from "lit"; +import { customElement, property, state } 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-visibility-editor"; + +const TABS = ["config", "visibility"] as const; @customElement("hui-card-element-editor") export class HuiCardElementEditor extends HuiElementEditor { + @state() private _curTab: (typeof TABS)[number] = TABS[0]; + + @property({ type: Boolean, attribute: "show-visibility-tab" }) + public showVisibilityTab = false; + protected async getConfigElement(): Promise { const elClass = await getCardElementClass(this.configElementType!); @@ -27,6 +38,73 @@ export class HuiCardElementEditor extends HuiElementEditor { return undefined; } + + private _handleTabSelected(ev: CustomEvent): void { + if (!ev.detail.value) { + return; + } + this._curTab = ev.detail.value.id; + } + + private _configChanged(ev: CustomEvent): void { + ev.stopPropagation(); + this.value = ev.detail.value; + } + + protected renderConfigElement(): TemplateResult { + if (!this.showVisibilityTab) return super.renderConfigElement(); + + let content: TemplateResult<1> | typeof nothing = nothing; + + switch (this._curTab) { + case "config": + content = html`${super.renderConfigElement()}`; + break; + case "visibility": + content = html` + + `; + break; + } + return html` + + ${TABS.map( + (tab, index) => html` + + ${this.hass.localize( + `ui.panel.lovelace.editor.edit_card.tab-${tab}` + )} + + ` + )} + + ${content} + `; + } + + static get styles(): CSSResultGroup { + return [ + HuiElementEditor.styles, + css` + paper-tabs { + --paper-tabs-selection-bar-color: var(--primary-color); + color: var(--primary-text-color); + text-transform: uppercase; + margin-bottom: 16px; + border-bottom: 1px solid var(--divider-color); + } + `, + ]; + } } declare global { diff --git a/src/panels/lovelace/editor/card-editor/hui-card-visibility-editor.ts b/src/panels/lovelace/editor/card-editor/hui-card-visibility-editor.ts new file mode 100644 index 000000000000..9e114dae57af --- /dev/null +++ b/src/panels/lovelace/editor/card-editor/hui-card-visibility-editor.ts @@ -0,0 +1,51 @@ +import { LitElement, html } from "lit"; +import { customElement, property } from "lit/decorators"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-alert"; +import { LovelaceCardConfig } from "../../../../data/lovelace/config/card"; +import { HomeAssistant } from "../../../../types"; +import { Condition } from "../../common/validate-condition"; +import "../conditions/ha-card-conditions-editor"; + +@customElement("hui-card-visibility-editor") +export class HuiCardVisibilityEditor extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public config!: LovelaceCardConfig; + + render() { + const conditions = this.config.visibility ?? []; + return html` + + ${this.hass.localize( + `ui.panel.lovelace.editor.edit_card.visibility.explanation` + )} + + + + `; + } + + private _valueChanged(ev: CustomEvent): void { + ev.stopPropagation(); + const conditions = ev.detail.value as Condition[]; + const newConfig: LovelaceCardConfig = { + ...this.config, + visibility: conditions, + }; + if (newConfig.visibility?.length === 0) { + delete newConfig.visibility; + } + fireEvent(this, "value-changed", { value: newConfig }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-card-visibility-editor": HuiCardVisibilityEditor; + } +} diff --git a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts index e437a81362b2..c2794e6f2cef 100644 --- a/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/card-editor/hui-dialog-edit-card.ts @@ -234,6 +234,7 @@ export class HuiDialogEditCard
extends LitElement { return this.value ? (this.value as any).type : undefined; } + protected renderConfigElement(): TemplateResult { + return html`${this._configElement}`; + } + protected render(): TemplateResult { return html`
@@ -211,7 +216,7 @@ export abstract class HuiElementEditor extends LitElement { class="center margin-bot" > ` - : this._configElement} + : this.renderConfigElement()}
` : html` diff --git a/src/panels/lovelace/sections/hui-section.ts b/src/panels/lovelace/sections/hui-section.ts index fa722fc29a50..5a1270138411 100644 --- a/src/panels/lovelace/sections/hui-section.ts +++ b/src/panels/lovelace/sections/hui-section.ts @@ -26,15 +26,22 @@ import { parseLovelaceCardPath } from "../editor/lovelace-path"; import { generateLovelaceSectionStrategy } from "../strategies/get-strategy"; import type { Lovelace } from "../types"; import { DEFAULT_SECTION_LAYOUT } from "./const"; +import { fireEvent } from "../../../common/dom/fire_event"; + +declare global { + interface HASSDomEvents { + "section-visibility-changed": { value: boolean }; + } +} @customElement("hui-section") export class HuiSection extends ReactiveElement { @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public lovelace!: Lovelace; - @property({ attribute: false }) public config!: LovelaceSectionRawConfig; + @property({ attribute: false }) public lovelace?: Lovelace; + @property({ type: Number }) public index!: number; @property({ type: Number }) public viewIndex!: number; @@ -215,12 +222,16 @@ export class HuiSection extends ReactiveElement { } const visible = forceVisible || - this.lovelace.editMode || + this.lovelace?.editMode || !this.config.visibility || checkConditionsMet(this.config.visibility, this.hass); - this.style.setProperty("display", visible ? "" : "none"); - this.toggleAttribute("hidden", !visible); + if (this.hidden !== !visible) { + this.style.setProperty("display", visible ? "" : "none"); + this.toggleAttribute("hidden", !visible); + fireEvent(this, "section-visibility-changed", { value: visible }); + } + if (!visible && this._layoutElement.parentElement) { this.removeChild(this._layoutElement); } else if (visible && !this._layoutElement.parentElement) { @@ -235,6 +246,7 @@ export class HuiSection extends ReactiveElement { this._layoutElementType = config.type; this._layoutElement.addEventListener("ll-create-card", (ev) => { ev.stopPropagation(); + if (!this.lovelace) return; showCreateCardDialog(this, { lovelaceConfig: this.lovelace.config, saveConfig: this.lovelace.saveConfig, @@ -244,6 +256,7 @@ export class HuiSection extends ReactiveElement { }); this._layoutElement.addEventListener("ll-edit-card", (ev) => { ev.stopPropagation(); + if (!this.lovelace) return; const { cardIndex } = parseLovelaceCardPath(ev.detail.path); showEditCardDialog(this, { lovelaceConfig: this.lovelace.config, @@ -254,6 +267,7 @@ export class HuiSection extends ReactiveElement { }); this._layoutElement.addEventListener("ll-delete-card", (ev) => { ev.stopPropagation(); + if (!this.lovelace) return; if (ev.detail.confirm) { confDeleteCard(this, this.hass!, this.lovelace!, ev.detail.path); } else { diff --git a/src/panels/lovelace/views/hui-sections-view.ts b/src/panels/lovelace/views/hui-sections-view.ts index df963000c5ac..d50e1af18f9c 100644 --- a/src/panels/lovelace/views/hui-sections-view.ts +++ b/src/panels/lovelace/views/hui-sections-view.ts @@ -54,29 +54,22 @@ export class SectionsView extends LitElement implements LovelaceViewElement { return this._sectionConfigKeys.get(sectionConfig)!; } - private _sectionObserver?: MutationObserver; - private _computeSectionsCount() { this._sectionCount = this.sections.filter( (section) => !section.hidden ).length; } + connectedCallback(): void { + super.connectedCallback(); + this.addEventListener("section-visibility-changed", () => { + this._computeSectionsCount(); + }); + } + willUpdate(changedProperties: PropertyValues): void { - if (!this._sectionObserver) { - this._sectionObserver = new MutationObserver(() => { - this._computeSectionsCount(); - }); - } if (changedProperties.has("sections")) { this._computeSectionsCount(); - this._sectionObserver.disconnect(); - this.sections.forEach((section) => { - this._sectionObserver!.observe(section, { - attributes: true, - attributeFilter: ["hidden"], - }); - }); } } diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index 08bd846a55ac..f74984d32b2f 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -74,8 +74,7 @@ export class HUIView extends ReactiveElement { private _viewConfigTheme?: string; - // Public to make demo happy - public createCardElement(cardConfig: LovelaceCardConfig) { + private _createCardElement(cardConfig: LovelaceCardConfig) { const element = document.createElement("hui-card"); element.hass = this.hass; element.lovelace = this.lovelace; @@ -371,7 +370,7 @@ export class HUIView extends ReactiveElement { } this._cards = config.cards.map((cardConfig) => { - const element = this.createCardElement(cardConfig); + const element = this._createCardElement(cardConfig); return element; }); } @@ -393,7 +392,7 @@ export class HUIView extends ReactiveElement { cardElToReplace: HuiCard, config: LovelaceCardConfig ): void { - const newCardEl = this.createCardElement(config); + const newCardEl = this._createCardElement(config); if (cardElToReplace.parentElement) { cardElToReplace.parentElement!.replaceChild(newCardEl, cardElToReplace); } diff --git a/src/translations/en.json b/src/translations/en.json index ec84ebd489e4..23be084a37a0 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5518,7 +5518,12 @@ "decrease_position": "Decrease card position", "increase_position": "Increase card position", "options": "More options", - "search_cards": "Search cards" + "search_cards": "Search cards", + "tab-config": "Config", + "tab-visibility": "Visibility", + "visibility": { + "explanation": "The card will be shown when ALL conditions below are fulfilled. If no conditions are set, the card will always be shown." + } }, "move_card": { "header": "Choose a view to move the card to", diff --git a/yarn.lock b/yarn.lock index d04f5745ccba..99be9a5c1730 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1461,9 +1461,9 @@ __metadata: languageName: node linkType: hard -"@codemirror/autocomplete@npm:6.16.0": - version: 6.16.0 - resolution: "@codemirror/autocomplete@npm:6.16.0" +"@codemirror/autocomplete@npm:6.16.2": + version: 6.16.2 + resolution: "@codemirror/autocomplete@npm:6.16.2" dependencies: "@codemirror/language": "npm:^6.0.0" "@codemirror/state": "npm:^6.0.0" @@ -1474,7 +1474,7 @@ __metadata: "@codemirror/state": ^6.0.0 "@codemirror/view": ^6.0.0 "@lezer/common": ^1.0.0 - checksum: 10/a29e27f69d17bce014d1d34ab4cd7c607b139cb3cd529aad554f2b0badb9d2924412b0ab3f4eb44901ea4250cdb033e0c6b81134ee8991bdbcd128ecb3b94d75 + checksum: 10/0573740e55d80347df92f9ef638dc2f0dbecb99db1740daae45942fe8c39d40b90a7417ae7bf35129743d7525509a2e97fd853795652ea8b82687a712b668761 languageName: node linkType: hard @@ -4600,15 +4600,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:7.10.0": - version: 7.10.0 - resolution: "@typescript-eslint/eslint-plugin@npm:7.10.0" +"@typescript-eslint/eslint-plugin@npm:7.11.0": + version: 7.11.0 + resolution: "@typescript-eslint/eslint-plugin@npm:7.11.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:7.10.0" - "@typescript-eslint/type-utils": "npm:7.10.0" - "@typescript-eslint/utils": "npm:7.10.0" - "@typescript-eslint/visitor-keys": "npm:7.10.0" + "@typescript-eslint/scope-manager": "npm:7.11.0" + "@typescript-eslint/type-utils": "npm:7.11.0" + "@typescript-eslint/utils": "npm:7.11.0" + "@typescript-eslint/visitor-keys": "npm:7.11.0" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -4619,44 +4619,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/dfe505cdf718dd29e8637b902e4c544c6b7d246d2051fd1936090423eb3dadfe2bd757de51e565e6fd80e74cf1918e191c26fee6df515100484ec3efd9b8d111 + checksum: 10/be95ed0bbd5b34c47239677ea39d531bcd8a18717a67d70a297bed5b0050b256159856bb9c1e894ac550d011c24bb5b4abf8056c5d70d0d5895f0cc1accd14ea languageName: node linkType: hard -"@typescript-eslint/parser@npm:7.10.0": - version: 7.10.0 - resolution: "@typescript-eslint/parser@npm:7.10.0" +"@typescript-eslint/parser@npm:7.11.0": + version: 7.11.0 + resolution: "@typescript-eslint/parser@npm:7.11.0" dependencies: - "@typescript-eslint/scope-manager": "npm:7.10.0" - "@typescript-eslint/types": "npm:7.10.0" - "@typescript-eslint/typescript-estree": "npm:7.10.0" - "@typescript-eslint/visitor-keys": "npm:7.10.0" + "@typescript-eslint/scope-manager": "npm:7.11.0" + "@typescript-eslint/types": "npm:7.11.0" + "@typescript-eslint/typescript-estree": "npm:7.11.0" + "@typescript-eslint/visitor-keys": "npm:7.11.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10/1fa71049b2debf2f7f5366fb433e3d4c8e1591c2061a15fa8797d14623a2b6984340a59e7717acc013ce8c6a2ed32c5c0e811fe948b5936d41c2a5a09b61d130 + checksum: 10/0a32417aec62d7de04427323ab3fc8159f9f02429b24f739d8748e8b54fc65b0e3dbae8e4779c4b795f0d8e5f98a4d83a43b37ea0f50ebda51546cdcecf73caa languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.10.0": - version: 7.10.0 - resolution: "@typescript-eslint/scope-manager@npm:7.10.0" +"@typescript-eslint/scope-manager@npm:7.11.0": + version: 7.11.0 + resolution: "@typescript-eslint/scope-manager@npm:7.11.0" dependencies: - "@typescript-eslint/types": "npm:7.10.0" - "@typescript-eslint/visitor-keys": "npm:7.10.0" - checksum: 10/838a7a9573577d830b2f65801ce045abe6fad08ac7e04bac4cc9b2e5b7cbac07e645de9c79b9485f4cc361fe25da5319025aa0336fad618023fff62e4e980638 + "@typescript-eslint/types": "npm:7.11.0" + "@typescript-eslint/visitor-keys": "npm:7.11.0" + checksum: 10/79eff310405c6657ff092641e3ad51c6698c6708b915ecef945ebdd1737bd48e1458c5575836619f42dec06143ec0e3a826f3e551af590d297367da3d08f329e languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.10.0": - version: 7.10.0 - resolution: "@typescript-eslint/type-utils@npm:7.10.0" +"@typescript-eslint/type-utils@npm:7.11.0": + version: 7.11.0 + resolution: "@typescript-eslint/type-utils@npm:7.11.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:7.10.0" - "@typescript-eslint/utils": "npm:7.10.0" + "@typescript-eslint/typescript-estree": "npm:7.11.0" + "@typescript-eslint/utils": "npm:7.11.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.3.0" peerDependencies: @@ -4664,23 +4664,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/e62db9ffbfbccce60258108f7ed025005e04df18da897ff1b30049e3c10a47150e94c2fb5ac0ab9711ebb60517521213dcccbea6d08125107a87a67088a79042 + checksum: 10/ab6ebeff68a60fc40d0ace88e03d6b4242b8f8fe2fa300db161780d58777b57f69fa077cd482e1b673316559459bd20b8cc89a7f9f30e644bfed8293f77f0e4b languageName: node linkType: hard -"@typescript-eslint/types@npm:7.10.0": - version: 7.10.0 - resolution: "@typescript-eslint/types@npm:7.10.0" - checksum: 10/76075a7b87ddfff8e7e4aebf3d225e67bf79ead12a7709999d4d5c31611d9c0813ca69a9298f320efb018fe493ce3763c964a0e670a4c953d8eff000f10672c0 +"@typescript-eslint/types@npm:7.11.0": + version: 7.11.0 + resolution: "@typescript-eslint/types@npm:7.11.0" + checksum: 10/c6a0b47ef43649a59c9d51edfc61e367b55e519376209806b1c98385a8385b529e852c7a57e081fb15ef6a5dc0fc8e90bd5a508399f5ac2137f4d462e89cdc30 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.10.0": - version: 7.10.0 - resolution: "@typescript-eslint/typescript-estree@npm:7.10.0" +"@typescript-eslint/typescript-estree@npm:7.11.0": + version: 7.11.0 + resolution: "@typescript-eslint/typescript-estree@npm:7.11.0" dependencies: - "@typescript-eslint/types": "npm:7.10.0" - "@typescript-eslint/visitor-keys": "npm:7.10.0" + "@typescript-eslint/types": "npm:7.11.0" + "@typescript-eslint/visitor-keys": "npm:7.11.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -4690,31 +4690,31 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/d11d0c45749c9bd4a187b6dfdf5600e36ba8c87667cd2020d9158667c47c32ec0bcb1ef3b7eee5577b667def5f7f33d8131092a0f221b3d3e8105078800f923f + checksum: 10/b98b101e42d3b91003510a5c5a83f4350b6c1cf699bf2e409717660579ffa71682bc280c4f40166265c03f9546ed4faedc3723e143f1ab0ed7f5990cc3dff0ae languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.10.0": - version: 7.10.0 - resolution: "@typescript-eslint/utils@npm:7.10.0" +"@typescript-eslint/utils@npm:7.11.0": + version: 7.11.0 + resolution: "@typescript-eslint/utils@npm:7.11.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:7.10.0" - "@typescript-eslint/types": "npm:7.10.0" - "@typescript-eslint/typescript-estree": "npm:7.10.0" + "@typescript-eslint/scope-manager": "npm:7.11.0" + "@typescript-eslint/types": "npm:7.11.0" + "@typescript-eslint/typescript-estree": "npm:7.11.0" peerDependencies: eslint: ^8.56.0 - checksum: 10/62327b585295f9c3aa2508aefac639d562b6f7f270a229aa3a2af8dbd055f4a4d230a8facae75a8a53bb8222b0041162072d259add56b541f8bdfda8da36ea5f + checksum: 10/fbef14e166a70ccc4527c0731e0338acefa28218d1a018aa3f5b6b1ad9d75c56278d5f20bda97cf77da13e0a67c4f3e579c5b2f1c2e24d676960927921b55851 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.10.0": - version: 7.10.0 - resolution: "@typescript-eslint/visitor-keys@npm:7.10.0" +"@typescript-eslint/visitor-keys@npm:7.11.0": + version: 7.11.0 + resolution: "@typescript-eslint/visitor-keys@npm:7.11.0" dependencies: - "@typescript-eslint/types": "npm:7.10.0" + "@typescript-eslint/types": "npm:7.11.0" eslint-visitor-keys: "npm:^3.4.3" - checksum: 10/44b555a075bdff38e3e13c454ceaac50aa2546635e81f907d1ea84822c8887487d1d6bb4ff690f627da9585dc19ad07e228847c162c30bb06c46fb119899d8cc + checksum: 10/1f2cf1214638e9e78e052393c9e24295196ec4781b05951659a3997e33f8699a760ea3705c17d770e10eda2067435199e0136ab09e5fac63869e22f2da184d89 languageName: node linkType: hard @@ -7631,16 +7631,16 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-lit@npm:1.13.0, eslint-plugin-lit@npm:^1.10.1": - version: 1.13.0 - resolution: "eslint-plugin-lit@npm:1.13.0" +"eslint-plugin-lit@npm:1.14.0, eslint-plugin-lit@npm:^1.10.1": + version: 1.14.0 + resolution: "eslint-plugin-lit@npm:1.14.0" dependencies: parse5: "npm:^6.0.1" parse5-htmlparser2-tree-adapter: "npm:^6.0.1" requireindex: "npm:^1.2.0" peerDependencies: eslint: ">= 5" - checksum: 10/e8d2bca442ec7c1436e5a6299df8e8ee980a5f8db9a1c7bff8fcc0e3983a133303134ace398e6695b4b96c3b384f2ed0601fa7d0ad15ffe9421922c31df5349a + checksum: 10/25daccae8b38970b881044ee72d4e4da82de5ed5cc466ce31209925903326d74a5bf50cb8c6c9d268a16eac178e377a83201f9ca777bc79355a49c1ea9d91364 languageName: node linkType: hard @@ -8924,7 +8924,7 @@ __metadata: "@babel/runtime": "npm:7.24.6" "@braintree/sanitize-url": "npm:7.0.2" "@bundle-stats/plugin-webpack-filter": "npm:4.13.2" - "@codemirror/autocomplete": "npm:6.16.0" + "@codemirror/autocomplete": "npm:6.16.2" "@codemirror/commands": "npm:6.5.0" "@codemirror/language": "npm:6.10.1" "@codemirror/legacy-modes": "npm:6.4.0" @@ -9014,8 +9014,8 @@ __metadata: "@types/tar": "npm:6.1.13" "@types/ua-parser-js": "npm:0.7.39" "@types/webspeechapi": "npm:0.0.29" - "@typescript-eslint/eslint-plugin": "npm:7.10.0" - "@typescript-eslint/parser": "npm:7.10.0" + "@typescript-eslint/eslint-plugin": "npm:7.11.0" + "@typescript-eslint/parser": "npm:7.11.0" "@vaadin/combo-box": "npm:24.3.13" "@vaadin/vaadin-themable-mixin": "npm:24.3.13" "@vibrant/color": "npm:3.2.1-alpha.1" @@ -9047,7 +9047,7 @@ __metadata: eslint-config-prettier: "npm:9.1.0" eslint-import-resolver-webpack: "npm:0.13.8" eslint-plugin-import: "npm:2.29.1" - eslint-plugin-lit: "npm:1.13.0" + eslint-plugin-lit: "npm:1.14.0" eslint-plugin-lit-a11y: "npm:4.1.2" eslint-plugin-unused-imports: "npm:4.0.0" eslint-plugin-wc: "npm:2.1.0" @@ -9104,7 +9104,7 @@ __metadata: stacktrace-js: "npm:2.0.2" superstruct: "npm:1.0.4" systemjs: "npm:6.15.1" - tar: "npm:7.1.0" + tar: "npm:7.2.0" terser-webpack-plugin: "npm:5.3.10" tinykeys: "npm:2.1.0" transform-async-modules-webpack-plugin: "npm:1.1.1" @@ -9112,7 +9112,7 @@ __metadata: tsparticles-engine: "npm:2.12.0" tsparticles-preset-links: "npm:2.12.0" typescript: "npm:5.4.5" - ua-parser-js: "npm:1.0.37" + ua-parser-js: "npm:1.0.38" unfetch: "npm:5.0.0" vis-data: "npm:7.1.9" vis-network: "npm:9.1.9" @@ -9125,7 +9125,7 @@ __metadata: webpack-stats-plugin: "npm:1.1.3" webpackbar: "npm:6.0.1" weekstart: "npm:2.0.0" - workbox-build: "npm:7.1.0" + workbox-build: "npm:7.1.1" workbox-cacheable-response: "npm:7.1.0" workbox-core: "npm:7.1.0" workbox-expiration: "npm:7.1.0" @@ -13723,9 +13723,9 @@ __metadata: languageName: node linkType: hard -"tar@npm:7.1.0": - version: 7.1.0 - resolution: "tar@npm:7.1.0" +"tar@npm:7.2.0": + version: 7.2.0 + resolution: "tar@npm:7.2.0" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" @@ -13733,7 +13733,7 @@ __metadata: minizlib: "npm:^3.0.1" mkdirp: "npm:^3.0.1" yallist: "npm:^5.0.0" - checksum: 10/87c2caa631dcf212cc50cc7fc4402b429d9083cf76f21d9ec63eba7a703b78804b2185bbe072f4c6cac8263dc8e7df786dfdfd53ef5bfbf7b46b480535794ea5 + checksum: 10/29e4ebd5057e754442921e3f46fdec3316e00a85009762c389c52d07fee11f1c85397849ae34949e43080c703d3c5ecfabffa6efcbb077315f72142a9b7a51da languageName: node linkType: hard @@ -14285,10 +14285,10 @@ __metadata: languageName: node linkType: hard -"ua-parser-js@npm:1.0.37": - version: 1.0.37 - resolution: "ua-parser-js@npm:1.0.37" - checksum: 10/56508f2428ebac64382c4d41da14189e5013e3e2a5f5918aff4bee3ba77df1f4eaad6f81f90c24999f1cf12cc1596764684497fec07e0ff5182ce9a323a8c05b +"ua-parser-js@npm:1.0.38": + version: 1.0.38 + resolution: "ua-parser-js@npm:1.0.38" + checksum: 10/f2345e9bd0f9c5f85bcaa434535fae88f4bb891538e568106f0225b2c2937fbfbeb5782bd22320d07b6b3d68b350b8861574c1d7af072ff9b2362fb72d326fd9 languageName: node linkType: hard @@ -15105,9 +15105,9 @@ __metadata: languageName: node linkType: hard -"workbox-build@npm:7.1.0": - version: 7.1.0 - resolution: "workbox-build@npm:7.1.0" +"workbox-build@npm:7.1.1": + version: 7.1.1 + resolution: "workbox-build@npm:7.1.1" dependencies: "@apideck/better-ajv-errors": "npm:^0.3.1" "@babel/core": "npm:^7.24.4" @@ -15146,7 +15146,7 @@ __metadata: workbox-streams: "npm:7.1.0" workbox-sw: "npm:7.1.0" workbox-window: "npm:7.1.0" - checksum: 10/6d2086899e65f7728fe3c2cc7d14dbc18bec2ae8c2e1e681f552e0162b8c138b2c2a235ddcf820b3b966cb06b60319fcaa9eb1831f35f1fab1da77fa4238dcbd + checksum: 10/65eb75fc67fd14f415e6978c1f6431c21d6b27bdaec7e5438d8586719c6b09aaad5cec6ecd89684d1d414ff3b52c4eb2de3d748e1e0873b5a6ab3a2531fe034b languageName: node linkType: hard