Compare commits

..

6 Commits

Author SHA1 Message Date
Paul Bottein
637eb6e894 Fix new script/automation dialog 2024-12-11 09:22:47 +01:00
Simon Lamon
f688780677 Combine Edit in yaml and Edit in visual editor (2/2) (#23143)
* yaml / visual part 2

* clean up

* ci
2024-12-11 09:49:15 +02:00
Simon Lamon
0ce98a86e6 Combine Edit in yaml and Edit in visual editor (1/2) (#23142)
* yaml / visual part 1

* clean up

* clean up
2024-12-11 09:48:10 +02:00
Jan-Philipp Benecke
bf624f5ca7 Intercept default search shortcut and focus our search input on data table pages (#23209)
Intercept default search shortcut on data table pages
2024-12-11 09:34:27 +02:00
renovate[bot]
ce5ce37de7 Update formatjs monorepo (#23250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 07:36:33 +01:00
Simon Zumbrunnen
da727d3a3a Added "Media player volume slider" card feature. (#23199)
* Added "Media player volume" card feature.

* Make sure the feature is not displayed on unsupported players

Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>

* Renamed to Media player volume *slider*

* Missed one rename.

---------

Co-authored-by: Simon Zumbrunnen <simon-zumbrunnen@users.noreply.github.com>
Co-authored-by: Petar Petrov <MindFreeze@users.noreply.github.com>
2024-12-10 13:36:56 +00:00
30 changed files with 499 additions and 844 deletions

View File

@@ -4,6 +4,7 @@ import { customElement, query } from "lit/decorators";
import { CoverEntityFeature } from "../../../../src/data/cover";
import { LightColorMode } from "../../../../src/data/light";
import { LockEntityFeature } from "../../../../src/data/lock";
import { MediaPlayerEntityFeature } from "../../../../src/data/media-player";
import { VacuumEntityFeature } from "../../../../src/data/vacuum";
import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass";
@@ -28,6 +29,10 @@ const ENTITIES = [
device_class: "lock",
supported_features: LockEntityFeature.OPEN,
}),
getEntity("media_player", "living_room", "playing", {
friendly_name: "Living room speaker",
supported_features: MediaPlayerEntityFeature.VOLUME_SET,
}),
getEntity("climate", "thermostat", "heat", {
current_temperature: 73,
min_temp: 45,
@@ -197,6 +202,15 @@ const CONFIGS = [
- type: "lock-open-door"
`,
},
{
heading: "Media player volume slider feature",
config: `
- type: tile
entity: media_player.living_room
features:
- type: "media-player-volume-slider"
`,
},
{
heading: "Vacuum commands feature",
config: `

View File

@@ -36,15 +36,15 @@
"@codemirror/state": "6.4.1",
"@codemirror/view": "6.35.2",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.16.5",
"@formatjs/intl-displaynames": "6.8.5",
"@formatjs/intl-durationformat": "0.6.4",
"@formatjs/intl-datetimeformat": "6.16.6",
"@formatjs/intl-displaynames": "6.8.6",
"@formatjs/intl-durationformat": "0.6.5",
"@formatjs/intl-getcanonicallocales": "2.5.3",
"@formatjs/intl-listformat": "7.7.5",
"@formatjs/intl-locale": "4.2.5",
"@formatjs/intl-numberformat": "8.14.5",
"@formatjs/intl-pluralrules": "5.3.5",
"@formatjs/intl-relativetimeformat": "11.4.5",
"@formatjs/intl-listformat": "7.7.6",
"@formatjs/intl-locale": "4.2.6",
"@formatjs/intl-numberformat": "8.14.6",
"@formatjs/intl-pluralrules": "5.3.6",
"@formatjs/intl-relativetimeformat": "11.4.6",
"@fullcalendar/core": "6.1.15",
"@fullcalendar/daygrid": "6.1.15",
"@fullcalendar/interaction": "6.1.15",
@@ -117,7 +117,7 @@
"hls.js": "patch:hls.js@npm%3A1.5.7#~/.yarn/patches/hls.js-npm-1.5.7-f5bbd3d060.patch",
"home-assistant-js-websocket": "9.4.0",
"idb-keyval": "6.2.1",
"intl-messageformat": "10.7.7",
"intl-messageformat": "10.7.8",
"js-yaml": "4.1.0",
"leaflet": "1.9.4",
"leaflet-draw": "patch:leaflet-draw@npm%3A1.0.4#./.yarn/patches/leaflet-draw-npm-1.0.4-0ca0ebcf65.patch",

View File

@@ -67,14 +67,11 @@ const loadCustomIconItems = async (iconsetPrefix: string) => {
}
};
const rowRenderer: ComboBoxLitRenderer<IconItem | RankedIcon> = (item) => html`
<ha-list-item graphic="avatar">
const rowRenderer: ComboBoxLitRenderer<IconItem | RankedIcon> = (item) =>
html`<ha-list-item graphic="avatar">
<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>
${item.icon}
</ha-list-item>
`;
export const CUSTOM_STATE_ICON = "___CUSTOM_STATE_ICON___";
</ha-list-item>`;
@customElement("ha-icon-picker")
export class HaIconPicker extends LitElement {
@@ -96,9 +93,6 @@ export class HaIconPicker extends LitElement {
@property({ type: Boolean }) public invalid = false;
@property({ type: Boolean, attribute: "force-fallback-icon" })
public forceFallbackIcon = false;
protected render(): TemplateResult {
return html`
<ha-combo-box
@@ -120,7 +114,7 @@ export class HaIconPicker extends LitElement {
@opened-changed=${this._openedChanged}
@value-changed=${this._valueChanged}
>
${(this._value || this.placeholder) && !this.forceFallbackIcon
${this._value || this.placeholder
? html`
<ha-icon .icon=${this._value || this.placeholder} slot="icon">
</ha-icon>

View File

@@ -35,6 +35,7 @@ export class HaIconSelector extends LitElement {
const placeholder =
this.selector.icon?.placeholder ||
stateObj?.attributes.icon ||
(stateObj && until(entityIcon(this.hass, stateObj)));
return html`

View File

@@ -20,8 +20,10 @@ export class HaStateIcon extends LitElement {
@property() public icon?: string;
protected render() {
const overrideIcon = this.icon;
const overrideIcon =
this.icon ||
(this.stateObj && this.hass?.entities[this.stateObj.entity_id]?.icon) ||
this.stateObj?.attributes.icon;
if (overrideIcon) {
return html`<ha-icon .icon=${overrideIcon}></ha-icon>`;
}

View File

@@ -12,14 +12,10 @@ import type { RegistryEntry } from "./registry";
type EntityCategory = "config" | "diagnostic";
export type EntityRegistryIcon = {
state?: Record<string, string>;
};
export interface EntityRegistryDisplayEntry {
entity_id: string;
name?: string;
icon?: EntityRegistryIcon | string;
icon?: string;
device_id?: string;
area_id?: string;
labels: string[];
@@ -51,7 +47,7 @@ export interface EntityRegistryEntry extends RegistryEntry {
id: string;
entity_id: string;
name: string | null;
icon: EntityRegistryIcon | string | null;
icon: string | null;
platform: string;
config_entry_id: string | null;
device_id: string | null;
@@ -132,7 +128,7 @@ export interface EntityRegistryOptions {
export interface EntityRegistryEntryUpdateParams {
name?: string | null;
icon?: string | EntityRegistryIcon | null;
icon?: string | null;
device_class?: string | null;
area_id?: string | null;
disabled_by?: string | null;

View File

@@ -178,36 +178,24 @@ export const getServiceIcons = async (
export const entityIcon = async (
hass: HomeAssistant,
stateObj: HassEntity,
stateValue?: string
): Promise<string | undefined> => {
state?: string
) => {
const entry = hass.entities?.[stateObj.entity_id] as
| EntityRegistryDisplayEntry
| undefined;
if (entry?.icon) {
if (typeof entry.icon === "string") {
return entry.icon;
}
const state = stateValue ?? stateObj.state;
if (entry.icon.state?.[state]) {
return entry.icon.state?.[state];
}
return entry.icon;
}
if (stateObj?.attributes.icon) {
return stateObj.attributes.icon;
}
const domain = computeStateDomain(stateObj);
return getEntityIcon(hass, domain, stateObj, stateValue, entry);
return getEntityIcon(hass, domain, stateObj, state, entry);
};
export const entryIcon = async (
hass: HomeAssistant,
entry: EntityRegistryEntry | EntityRegistryDisplayEntry
) => {
if (entry.icon && typeof entry.icon === "string") {
if (entry.icon) {
return entry.icon;
}
const stateObj = hass.states[entry.entity_id] as HassEntity | undefined;
@@ -215,7 +203,7 @@ export const entryIcon = async (
return getEntityIcon(hass, domain, stateObj, undefined, entry);
};
export const getEntityIcon = async (
const getEntityIcon = async (
hass: HomeAssistant,
domain: string,
stateObj?: HassEntity,

View File

@@ -39,9 +39,10 @@ import type { HomeAssistant, Route } from "../types";
import "./hass-tabs-subpage";
import type { PageNavigation } from "./hass-tabs-subpage";
import { showDataTableSettingsDialog } from "../components/data-table/show-dialog-data-table-settings";
import { KeyboardShortcutMixin } from "../mixins/keyboard-shortcut-mixin";
@customElement("hass-tabs-subpage-data-table")
export class HaTabsSubpageDataTable extends LitElement {
export class HaTabsSubpageDataTable extends KeyboardShortcutMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public localizeFunc?: LocalizeFunc;
@@ -189,6 +190,14 @@ export class HaTabsSubpageDataTable extends LitElement {
@query("#sort-by-menu") private _sortByMenu!: HaMenu;
@query("search-input-outlined") private _searchInput!: HTMLElement;
protected supportedShortcuts(): SupportedShortcuts {
return {
f: () => this._searchInput.focus(),
};
}
private _showPaneController = new ResizeController(this, {
callback: (entries) => entries[0]?.contentRect.width > 750,
});

View File

@@ -1,26 +1,35 @@
import type { LitElement } from "lit";
import type { Constructor } from "../types";
declare global {
interface SupportedShortcuts {
[key: string]: () => void;
}
}
export const KeyboardShortcutMixin = <T extends Constructor<LitElement>>(
superClass: T
) =>
class extends superClass {
private _keydownEvent = (event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.key === "s") {
const supportedShortcuts = this.supportedShortcuts();
if ((event.ctrlKey || event.metaKey) && event.key in supportedShortcuts) {
event.preventDefault();
this.handleKeyboardSave();
supportedShortcuts[event.key]();
}
};
public connectedCallback() {
super.connectedCallback();
this.addEventListener("keydown", this._keydownEvent);
window.addEventListener("keydown", this._keydownEvent);
}
public disconnectedCallback() {
this.removeEventListener("keydown", this._keydownEvent);
window.removeEventListener("keydown", this._keydownEvent);
super.disconnectedCallback();
}
protected handleKeyboardSave() {}
protected supportedShortcuts(): SupportedShortcuts {
return {};
}
};

View File

@@ -1,11 +1,9 @@
import { consume } from "@lit-labs/context";
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import {
mdiAlertCircleCheck,
mdiArrowDown,
mdiArrowUp,
mdiCheck,
mdiContentCopy,
mdiContentCut,
mdiContentDuplicate,
@@ -13,6 +11,7 @@ import {
mdiDotsVertical,
mdiPlay,
mdiPlayCircleOutline,
mdiPlaylistEdit,
mdiRenameBox,
mdiStopCircleOutline,
} from "@mdi/js";
@@ -253,23 +252,23 @@ export default class HaAutomationActionRow extends LitElement {
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item graphic="icon">
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.run"
)}
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.rename"
)}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
@@ -277,31 +276,31 @@ export default class HaAutomationActionRow extends LitElement {
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.copy"
)}
<ha-svg-icon slot="graphic" .path=${mdiContentCopy}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.cut"
)}
<ha-svg-icon slot="graphic" .path=${mdiContentCut}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || this.first}
>
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
<ha-svg-icon slot="graphic" .path=${mdiArrowUp}></ha-svg-icon
></mwc-list-item>
></ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || this.last}
>
@@ -309,37 +308,21 @@ export default class HaAutomationActionRow extends LitElement {
"ui.panel.config.automation.editor.move_down"
)}
<ha-svg-icon slot="graphic" .path=${mdiArrowDown}></ha-svg-icon
></mwc-list-item>
></ha-list-item>
<li divider role="separator"></li>
<mwc-list-item .disabled=${!this._uiModeAvailable} graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${!yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item .disabled=${!this._uiModeAvailable} graphic="icon">
<ha-list-item graphic="icon" .disabled=${!this._uiModeAvailable}>
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
)}
${yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<ha-svg-icon
slot="graphic"
.path=${mdiPlaylistEdit}
></ha-svg-icon>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.action.enabled === false
? this.hass.localize(
"ui.panel.config.automation.editor.actions.enable"
@@ -353,8 +336,8 @@ export default class HaAutomationActionRow extends LitElement {
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item
</ha-list-item>
<ha-list-item
class="warning"
graphic="icon"
.disabled=${this.disabled}
@@ -367,7 +350,7 @@ export default class HaAutomationActionRow extends LitElement {
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
</ha-button-menu>
<div
@@ -466,17 +449,17 @@ export default class HaAutomationActionRow extends LitElement {
fireEvent(this, "move-down");
break;
case 7:
this._switchUiMode();
if (this._yamlMode) {
this._switchUiMode();
} else {
this._switchYamlMode();
}
this.expand();
break;
case 8:
this._switchYamlMode();
this.expand();
break;
case 9:
this._onDisable();
break;
case 10:
case 9:
this._onDelete();
break;
}
@@ -676,9 +659,6 @@ export default class HaAutomationActionRow extends LitElement {
.warning ul {
margin: 4px 0;
}
.selected_menu_item {
color: var(--primary-color);
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}

View File

@@ -1,10 +1,8 @@
import { consume } from "@lit-labs/context";
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import {
mdiArrowDown,
mdiArrowUp,
mdiCheck,
mdiContentCopy,
mdiContentCut,
mdiContentDuplicate,
@@ -12,6 +10,7 @@ import {
mdiDotsVertical,
mdiFlask,
mdiPlayCircleOutline,
mdiPlaylistEdit,
mdiRenameBox,
mdiStopCircleOutline,
} from "@mdi/js";
@@ -29,6 +28,7 @@ import "../../../../components/ha-button-menu";
import "../../../../components/ha-card";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-list-item";
import type {
AutomationClipboard,
Condition,
@@ -155,22 +155,22 @@ export default class HaAutomationConditionRow extends LitElement {
>
</ha-icon-button>
<mwc-list-item graphic="icon">
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.test"
)}
<ha-svg-icon slot="graphic" .path=${mdiFlask}></ha-svg-icon>
</mwc-list-item>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
</ha-list-item>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.rename"
)}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.duplicate"
)}
@@ -178,31 +178,31 @@ export default class HaAutomationConditionRow extends LitElement {
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.copy"
)}
<ha-svg-icon slot="graphic" .path=${mdiContentCopy}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.cut"
)}
<ha-svg-icon slot="graphic" .path=${mdiContentCut}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || this.first}
>
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
<ha-svg-icon slot="graphic" .path=${mdiArrowUp}></ha-svg-icon
></mwc-list-item>
></ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || this.last}
>
@@ -210,37 +210,21 @@ export default class HaAutomationConditionRow extends LitElement {
"ui.panel.config.automation.editor.move_down"
)}
<ha-svg-icon slot="graphic" .path=${mdiArrowDown}></ha-svg-icon
></mwc-list-item>
></ha-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${!this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item graphic="icon">
<ha-list-item graphic="icon" .disabled=${this._warnings}>
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
)}
${this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<ha-svg-icon
slot="graphic"
.path=${mdiPlaylistEdit}
></ha-svg-icon>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.condition.enabled === false
? this.hass.localize(
"ui.panel.config.automation.editor.actions.enable"
@@ -254,8 +238,8 @@ export default class HaAutomationConditionRow extends LitElement {
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item
</ha-list-item>
<ha-list-item
class="warning"
graphic="icon"
.disabled=${this.disabled}
@@ -268,7 +252,7 @@ export default class HaAutomationConditionRow extends LitElement {
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
</ha-button-menu>
<div
@@ -366,17 +350,17 @@ export default class HaAutomationConditionRow extends LitElement {
fireEvent(this, "move-down");
break;
case 7:
this._switchUiMode();
if (this._yamlMode) {
this._switchUiMode();
} else {
this._switchYamlMode();
}
this.expand();
break;
case 8:
this._switchYamlMode();
this.expand();
break;
case 9:
this._onDisable();
break;
case 10:
case 9:
this._onDelete();
break;
}
@@ -555,10 +539,10 @@ export default class HaAutomationConditionRow extends LitElement {
border-top-right-radius: var(--ha-card-border-radius);
border-top-left-radius: var(--ha-card-border-radius);
}
mwc-list-item[disabled] {
ha-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
mwc-list-item.hidden {
ha-list-item.hidden {
display: none;
}
.testing {
@@ -587,9 +571,6 @@ export default class HaAutomationConditionRow extends LitElement {
.testing.pass {
background-color: var(--success-color);
}
.selected_menu_item {
color: var(--primary-color);
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}

View File

@@ -205,24 +205,24 @@ class DialogNewAutomation extends LitElement implements HassDialog {
return;
}
const path = (ev.currentTarget! as any).path;
this.closeDialog();
if (this._mode === "script") {
showScriptEditor({ use_blueprint: { path } });
} else {
showAutomationEditor({ use_blueprint: { path } });
}
this.closeDialog();
}
private async _blank(ev) {
if (!shouldHandleRequestSelectedEvent(ev)) {
return;
}
this.closeDialog();
if (this._mode === "script") {
showScriptEditor();
} else {
showAutomationEditor();
}
this.closeDialog();
}
static get styles(): CSSResultGroup {

View File

@@ -1,6 +1,5 @@
import "@material/mwc-button";
import {
mdiCheck,
mdiContentDuplicate,
mdiContentSave,
mdiDebugStepOver,
@@ -10,6 +9,7 @@ import {
mdiInformationOutline,
mdiPlay,
mdiPlayCircleOutline,
mdiPlaylistEdit,
mdiRenameBox,
mdiRobotConfused,
mdiStopCircleOutline,
@@ -259,27 +259,16 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
`
: nothing}
<li divider role="separator"></li>
<ha-list-item graphic="icon" @click=${this._switchUiMode}>
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${this._mode === "gui"
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</ha-list-item>
<ha-list-item graphic="icon" @click=${this._switchYamlMode}>
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
${this._mode === "yaml"
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
<ha-list-item
graphic="icon"
@click=${this._mode === "gui"
? this._switchYamlMode
: this._switchUiMode}
>
${this.hass.localize(
`ui.panel.config.automation.editor.edit_${this._mode === "gui" ? "yaml" : "ui"}`
)}
<ha-svg-icon slot="graphic" .path=${mdiPlaylistEdit}></ha-svg-icon>
</ha-list-item>
<li divider role="separator"></li>
@@ -852,8 +841,10 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
ev.detail.callback(this._config);
}
protected handleKeyboardSave() {
this._saveAutomation();
protected supportedShortcuts(): SupportedShortcuts {
return {
s: () => this._saveAutomation(),
};
}
static get styles(): CSSResultGroup {
@@ -901,9 +892,6 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
ha-fab.dirty {
bottom: 0;
}
.selected_menu_item {
color: var(--primary-color);
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}

View File

@@ -1,10 +1,8 @@
import { consume } from "@lit-labs/context";
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import {
mdiArrowDown,
mdiArrowUp,
mdiCheck,
mdiContentCopy,
mdiContentCut,
mdiContentDuplicate,
@@ -12,6 +10,7 @@ import {
mdiDotsVertical,
mdiIdentifier,
mdiPlayCircleOutline,
mdiPlaylistEdit,
mdiRenameBox,
mdiStopCircleOutline,
} from "@mdi/js";
@@ -34,6 +33,7 @@ import "../../../../components/ha-card";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-textfield";
import "../../../../components/ha-list-item";
import type { HaYamlEditor } from "../../../../components/ha-yaml-editor";
import type { AutomationClipboard, Trigger } from "../../../../data/automation";
import {
@@ -182,7 +182,7 @@ export default class HaAutomationTriggerRow extends LitElement {
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || type === "list"}
>
@@ -190,9 +190,9 @@ export default class HaAutomationTriggerRow extends LitElement {
"ui.panel.config.automation.editor.triggers.rename"
)}
<ha-svg-icon slot="graphic" .path=${mdiRenameBox}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || type === "list"}
>
@@ -200,11 +200,11 @@ export default class HaAutomationTriggerRow extends LitElement {
"ui.panel.config.automation.editor.triggers.edit_id"
)}
<ha-svg-icon slot="graphic" .path=${mdiIdentifier}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.duplicate"
)}
@@ -212,31 +212,31 @@ export default class HaAutomationTriggerRow extends LitElement {
slot="graphic"
.path=${mdiContentDuplicate}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.copy"
)}
<ha-svg-icon slot="graphic" .path=${mdiContentCopy}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item graphic="icon" .disabled=${this.disabled}>
<ha-list-item graphic="icon" .disabled=${this.disabled}>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.cut"
)}
<ha-svg-icon slot="graphic" .path=${mdiContentCut}></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || this.first}
>
${this.hass.localize("ui.panel.config.automation.editor.move_up")}
<ha-svg-icon slot="graphic" .path=${mdiArrowUp}></ha-svg-icon
></mwc-list-item>
></ha-list-item>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || this.last}
>
@@ -244,37 +244,21 @@ export default class HaAutomationTriggerRow extends LitElement {
"ui.panel.config.automation.editor.move_down"
)}
<ha-svg-icon slot="graphic" .path=${mdiArrowDown}></ha-svg-icon
></mwc-list-item>
></ha-list-item>
<li divider role="separator"></li>
<mwc-list-item .disabled=${!supported} graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${!yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item .disabled=${!supported} graphic="icon">
<ha-list-item graphic="icon" .disabled=${!supported}>
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
`ui.panel.config.automation.editor.edit_${!yamlMode ? "yaml" : "ui"}`
)}
${yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<ha-svg-icon
slot="graphic"
.path=${mdiPlaylistEdit}
></ha-svg-icon>
</ha-list-item>
<li divider role="separator"></li>
<mwc-list-item
<ha-list-item
graphic="icon"
.disabled=${this.disabled || type === "list"}
>
@@ -292,8 +276,8 @@ export default class HaAutomationTriggerRow extends LitElement {
? mdiPlayCircleOutline
: mdiStopCircleOutline}
></ha-svg-icon>
</mwc-list-item>
<mwc-list-item
</ha-list-item>
<ha-list-item
class="warning"
graphic="icon"
.disabled=${this.disabled}
@@ -306,7 +290,7 @@ export default class HaAutomationTriggerRow extends LitElement {
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
</ha-button-menu>
<div
@@ -506,17 +490,17 @@ export default class HaAutomationTriggerRow extends LitElement {
fireEvent(this, "move-down");
break;
case 7:
this._switchUiMode();
if (this._yamlMode) {
this._switchUiMode();
} else {
this._switchYamlMode();
}
this.expand();
break;
case 8:
this._switchYamlMode();
this.expand();
break;
case 9:
this._onDisable();
break;
case 10:
case 9:
this._onDelete();
break;
}
@@ -730,19 +714,16 @@ export default class HaAutomationTriggerRow extends LitElement {
background-color: var(--accent-color);
color: var(--text-accent-color, var(--text-primary-color));
}
mwc-list-item[disabled] {
ha-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
mwc-list-item.hidden {
ha-list-item.hidden {
display: none;
}
ha-textfield {
display: block;
margin-bottom: 24px;
}
.selected_menu_item {
color: var(--primary-color);
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}

View File

@@ -1,170 +0,0 @@
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
import { getStates } from "../../../../common/entity/get_states";
import "../../../../components/ha-alert";
import "../../../../components/ha-area-picker";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog";
import "../../../../components/ha-labels-picker";
import "../../../../components/ha-textfield";
import "../../../../components/ha-yaml-editor";
import type { EntityRegistryIcon } from "../../../../data/entity_registry";
import { getEntityIcon } from "../../../../data/icons";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { EntityStateIconDialogParams } from "./show-dialog-entity-state-icon";
@customElement("dialog-entity-state-icon")
class DialogEntityStateIcon extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _params?: EntityStateIconDialogParams;
@state() private _submitting = false;
@state() private _config?: EntityRegistryIcon;
public async showDialog(params: EntityStateIconDialogParams): Promise<void> {
this._params = params;
const icon = this._params.icon;
this._config = typeof icon === "object" ? icon : {};
await this.updateComplete;
}
public closeDialog(): void {
this._params = undefined;
this._config = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
private get _states() {
const entity = this.hass.states[this._params!.entry.entity_id];
const states = getStates(entity);
return states.filter((s) => !["unknown", "unavailable"].includes(s));
}
protected render() {
if (!this._params || !this._config) {
return nothing;
}
const stateObj = this.hass.states[this._params.entry.entity_id];
const domain = computeStateDomain(stateObj);
return html`
<ha-dialog
open
@closed=${this.closeDialog}
.heading=${"Entity state icon"}
>
${this._states.map((s) => {
const value = this._config?.state?.[s];
const placeholder = until(
getEntityIcon(this.hass, domain, stateObj, s, this._params!.entry)
);
return html`
<div class="row">
<p>${this.hass.formatEntityState(stateObj, s)}</p>
<ha-icon-picker
.hass=${this.hass}
.id=${s}
.value=${value}
@value-changed=${this._iconChanged}
.placeholder=${placeholder}
></ha-icon-picker>
</div>
`;
})}
<ha-button
slot="secondaryAction"
@click=${this.closeDialog}
.disabled=${this._submitting}
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button
slot="primaryAction"
@click=${this._updateEntry}
.disabled=${this._submitting}
>
${this.hass.localize("ui.dialogs.device-registry-detail.update")}
</ha-button>
</ha-dialog>
`;
}
private _iconChanged(ev): void {
const value = ev.detail.value;
const id = ev.currentTarget.id;
const newConfig = {
...this._config!,
state: {
...this._config!.state,
[id]: value,
},
};
if (!value) {
delete newConfig.state[id];
}
this._config = newConfig;
}
private async _updateEntry(): Promise<void> {
this._submitting = true;
try {
const icon =
!this._config || Object.keys(this._config).length === 0
? null
: this._config;
this._params!.updateIcon(icon);
this.closeDialog();
} catch (err: any) {
// eslint-disable-next-line no-console
console.error(err);
} finally {
this._submitting = false;
}
}
static get styles(): CSSResultGroup {
return [
haStyle,
haStyleDialog,
css`
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
}
.row p {
width: 100px;
}
.row ha-icon-picker {
flex: 1;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-entity-state-icon": DialogEntityStateIcon;
}
}

View File

@@ -1,25 +0,0 @@
import { fireEvent } from "../../../../common/dom/fire_event";
import type {
EntityRegistryEntry,
EntityRegistryIcon,
} from "../../../../data/entity_registry";
export interface EntityStateIconDialogParams {
entry: EntityRegistryEntry;
icon: string | EntityRegistryIcon;
updateIcon: (icons: EntityRegistryIcon | null) => Promise<unknown>;
}
export const loadEntityStateIconDialog = () =>
import("./dialog-entity-state-icon");
export const showEntityStateIconDialog = (
element: HTMLElement,
params: EntityStateIconDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-entity-state-icon",
dialogImport: loadEntityStateIconDialog,
dialogParams: params,
});
};

View File

@@ -1,6 +1,6 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-formfield/mwc-formfield";
import { mdiBrush, mdiContentCopy } from "@mdi/js";
import { mdiContentCopy } from "@mdi/js";
import type { HassEntity } from "home-assistant-js-websocket";
import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
@@ -25,13 +25,13 @@ import "../../../components/ha-area-picker";
import "../../../components/ha-icon";
import "../../../components/ha-icon-button-next";
import "../../../components/ha-icon-picker";
import "../../../components/ha-labels-picker";
import "../../../components/ha-list-item";
import "../../../components/ha-radio";
import "../../../components/ha-select";
import "../../../components/ha-settings-row";
import "../../../components/ha-state-icon";
import "../../../components/ha-switch";
import "../../../components/ha-labels-picker";
import type { HaSwitch } from "../../../components/ha-switch";
import "../../../components/ha-textfield";
import {
@@ -56,7 +56,6 @@ import type {
AlarmControlPanelEntityOptions,
EntityRegistryEntry,
EntityRegistryEntryUpdateParams,
EntityRegistryIcon,
ExtEntityRegistryEntry,
LockEntityOptions,
SensorEntityOptions,
@@ -92,7 +91,6 @@ import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { showToast } from "../../../util/toast";
import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail";
import { showEntityStateIconDialog } from "./dialogs/show-dialog-entity-state-icon";
const OVERRIDE_DEVICE_CLASSES = {
cover: [
@@ -151,7 +149,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
@state() private _name!: string;
@state() private _icon!: string | EntityRegistryIcon;
@state() private _icon!: string;
@state() private _entityId!: string;
@@ -351,53 +349,6 @@ export class EntityRegistrySettingsEditor extends LitElement {
}
}
private _renderIconPicker(stateObj: HassEntity) {
const useStateIcon = typeof this._icon === "object";
const value = useStateIcon ? "Custom state icons" : this._icon;
const placeholder =
this.entry.original_icon ||
(stateObj && until(entityIcon(this.hass, stateObj))) ||
until(entryIcon(this.hass, this.entry));
return html`
<div class="icon-container">
<ha-icon-picker
.hass=${this.hass}
.value=${value}
@value-changed=${this._iconChanged}
.label=${this.hass.localize("ui.dialogs.entity_registry.editor.icon")}
.placeholder=${placeholder}
.disabled=${this.disabled}
.forceFallbackIcon=${useStateIcon}
>
${useStateIcon
? html`
<ha-state-icon
slot="fallback"
.hass=${this.hass}
.stateObj=${stateObj}
></ha-state-icon>
`
: !this._icon && !stateObj?.attributes.icon && stateObj
? html`
<ha-state-icon
slot="fallback"
.hass=${this.hass}
.stateObj=${stateObj}
></ha-state-icon>
`
: nothing}
</ha-icon-picker>
<ha-icon-button
.path=${mdiBrush}
@click=${this._openEntityStateIcon}
></ha-icon-button>
</div>
`;
}
protected render() {
if (this.entry.entity_id !== this._origEntityId) {
return nothing;
@@ -429,7 +380,33 @@ export class EntityRegistrySettingsEditor extends LitElement {
.placeholder=${this.entry.original_name}
@input=${this._nameChanged}
></ha-textfield>`}
${this.hideIcon ? nothing : this._renderIconPicker(stateObj)}
${this.hideIcon
? nothing
: html`
<ha-icon-picker
.hass=${this.hass}
.value=${this._icon}
@value-changed=${this._iconChanged}
.label=${this.hass.localize(
"ui.dialogs.entity_registry.editor.icon"
)}
.placeholder=${this.entry.original_icon ||
stateObj?.attributes.icon ||
(stateObj && until(entityIcon(this.hass, stateObj))) ||
until(entryIcon(this.hass, this.entry))}
.disabled=${this.disabled}
>
${!this._icon && !stateObj?.attributes.icon && stateObj
? html`
<ha-state-icon
slot="fallback"
.hass=${this.hass}
.stateObj=${stateObj}
></ha-state-icon>
`
: nothing}
</ha-icon-picker>
`}
${domain === "switch"
? html`<ha-select
.label=${this.hass.localize(
@@ -1048,8 +1025,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
const params: Partial<EntityRegistryEntryUpdateParams> = {
name: this._name.trim() || null,
icon:
typeof this._icon === "string" ? this._icon.trim() : this._icon || null,
icon: this._icon.trim() || null,
area_id: this._areaId || null,
labels: this._labels || [],
new_entity_id: this._entityId.trim(),
@@ -1479,17 +1455,6 @@ export class EntityRegistrySettingsEditor extends LitElement {
});
}
private _openEntityStateIcon() {
showEntityStateIconDialog(this, {
entry: this.entry!,
icon: this._icon,
updateIcon: async (icon) => {
this._icon = icon || "";
fireEvent(this, "change");
},
});
}
private _handleVoiceAssistantsClicked() {
showVoiceAssistantsView(
this,
@@ -1584,18 +1549,6 @@ export class EntityRegistrySettingsEditor extends LitElement {
overflow: hidden;
--mdc-list-side-padding: 13px;
}
.icon-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.icon-container ha-button-icon {
flex: none;
}
.icon-container ha-icon-picker {
flex: 1;
}
`,
];
}

View File

@@ -1,7 +1,6 @@
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list";
import {
mdiCheck,
mdiCog,
mdiContentDuplicate,
mdiContentSave,
@@ -11,6 +10,7 @@ import {
mdiInformationOutline,
mdiMotionPlayOutline,
mdiPlay,
mdiPlaylistEdit,
mdiTag,
} from "@mdi/js";
import type { HassEvent } from "home-assistant-js-websocket";
@@ -263,16 +263,11 @@ export class HaSceneEditor extends SubscribeMixin(
.disabled=${!this.sceneId || this._mode === "live"}
>
${this.hass.localize("ui.panel.config.scene.picker.apply")}
<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiPlay}
></ha-svg-icon>
<ha-svg-icon slot="graphic" .path=${mdiPlay}></ha-svg-icon>
</ha-list-item>
<ha-list-item graphic="icon" .disabled=${!this.sceneId}>
${this.hass.localize("ui.panel.config.scene.picker.show_info")}
<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiInformationOutline}
></ha-svg-icon>
@@ -281,45 +276,21 @@ export class HaSceneEditor extends SubscribeMixin(
${this.hass.localize(
"ui.panel.config.automation.picker.show_settings"
)}
<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCog}
></ha-svg-icon>
<ha-svg-icon slot="graphic" .path=${mdiCog}></ha-svg-icon>
</ha-list-item>
<ha-list-item graphic="icon" .disabled=${!this.sceneId}>
${this.hass.localize(
`ui.panel.config.scene.picker.${this._getCategory(this._entityRegistryEntries, this._scene?.entity_id) ? "edit_category" : "assign_category"}`
)}
<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiTag}
></ha-svg-icon>
<ha-svg-icon slot="graphic" .path=${mdiTag}></ha-svg-icon>
</ha-list-item>
<li divider role="separator"></li>
<ha-list-item graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${this._mode !== "yaml"
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: nothing}
</ha-list-item>
<ha-list-item graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
${this._mode === "yaml"
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: nothing}
${this.hass.localize(
`ui.panel.config.automation.editor.edit_${this._mode !== "yaml" ? "yaml" : "ui"}`
)}
<ha-svg-icon slot="graphic" .path=${mdiPlaylistEdit}></ha-svg-icon>
</ha-list-item>
<li divider role="separator"></li>
@@ -717,17 +688,14 @@ export class HaSceneEditor extends SubscribeMixin(
if (this._mode === "yaml") {
this._initEntities(this._config!);
this._exitYamlMode();
}
break;
case 5:
if (this._mode !== "yaml") {
} else {
this._enterYamlMode();
}
break;
case 6:
case 5:
this._duplicate();
break;
case 7:
case 6:
this._deleteTapped();
break;
}
@@ -1215,8 +1183,10 @@ export class HaSceneEditor extends SubscribeMixin(
}
}
protected handleKeyboardSave() {
this._saveScene();
protected supportedShortcuts(): SupportedShortcuts {
return {
s: () => this._saveScene(),
};
}
private get _sceneAreaIdWithUpdates(): string | undefined | null {

View File

@@ -1,6 +1,5 @@
import "@material/mwc-button";
import {
mdiCheck,
mdiContentDuplicate,
mdiContentSave,
mdiDebugStepOver,
@@ -10,6 +9,7 @@ import {
mdiFormTextbox,
mdiInformationOutline,
mdiPlay,
mdiPlaylistEdit,
mdiRenameBox,
mdiRobotConfused,
mdiTransitConnection,
@@ -245,27 +245,16 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
`
: nothing}
<li divider role="separator"></li>
<ha-list-item graphic="icon" @click=${this._switchUiMode}>
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${this._mode === "gui"
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon> `
: ``}
</ha-list-item>
<ha-list-item graphic="icon" @click=${this._switchYamlMode}>
${this.hass.localize("ui.panel.config.automation.editor.edit_yaml")}
${this._mode === "yaml"
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
<ha-list-item
graphic="icon"
@click=${this._mode === "gui"
? this._switchYamlMode
: this._switchUiMode}
>
${this.hass.localize(
`ui.panel.config.automation.editor.edit_${this._mode === "gui" ? "yaml" : "ui"}`
)}
<ha-svg-icon slot="graphic" .path=${mdiPlaylistEdit}></ha-svg-icon>
</ha-list-item>
<li divider role="separator"></li>
@@ -818,8 +807,10 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
}
}
protected handleKeyboardSave() {
this._saveScript();
protected supportedShortcuts(): SupportedShortcuts {
return {
s: () => this._saveScript(),
};
}
static get styles(): CSSResultGroup {
@@ -872,9 +863,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
ha-fab.dirty {
bottom: 0;
}
.selected_menu_item {
color: var(--primary-color);
}
li[role="separator"] {
border-bottom-color: var(--divider-color);
}

View File

@@ -1,6 +1,5 @@
import type { ActionDetail } from "@material/mwc-list/mwc-list-foundation";
import "@material/mwc-list/mwc-list-item";
import { mdiCheck, mdiDelete, mdiDotsVertical } from "@mdi/js";
import { mdiDelete, mdiDotsVertical, mdiPlaylistEdit } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
@@ -13,6 +12,7 @@ import "../../../components/ha-button-menu";
import "../../../components/ha-card";
import "../../../components/ha-form/ha-form";
import "../../../components/ha-expansion-panel";
import "../../../components/ha-list-item";
import type { SchemaUnion } from "../../../components/ha-form/types";
import "../../../components/ha-icon-button";
import "../../../components/ha-yaml-editor";
@@ -98,31 +98,17 @@ export default class HaScriptFieldRow extends LitElement {
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item graphic="icon">
${this.hass.localize("ui.panel.config.automation.editor.edit_ui")}
${!this._yamlMode
? html` <ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item graphic="icon">
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.config.automation.editor.edit_yaml"
`ui.panel.config.automation.editor.edit_${!this._yamlMode ? "yaml" : "ui"}`
)}
${this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<ha-svg-icon
slot="graphic"
.path=${mdiPlaylistEdit}
></ha-svg-icon>
</ha-list-item>
<mwc-list-item
<ha-list-item
class="warning"
graphic="icon"
.disabled=${this.disabled}
@@ -135,7 +121,7 @@ export default class HaScriptFieldRow extends LitElement {
slot="graphic"
.path=${mdiDelete}
></ha-svg-icon>
</mwc-list-item>
</ha-list-item>
</ha-button-menu>
<div
class=${classMap({
@@ -174,12 +160,9 @@ export default class HaScriptFieldRow extends LitElement {
private async _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._yamlMode = false;
this._yamlMode = !this._yamlMode;
break;
case 1:
this._yamlMode = true;
break;
case 2:
this._onDelete();
break;
}
@@ -348,7 +331,7 @@ export default class HaScriptFieldRow extends LitElement {
border-top-left-radius: var(--ha-card-border-radius);
}
mwc-list-item[disabled] {
ha-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
.warning ul {

View File

@@ -0,0 +1,97 @@
import type { HassEntity } from "home-assistant-js-websocket";
import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeDomain } from "../../../common/entity/compute_domain";
import { stateActive } from "../../../common/entity/state_active";
import "../../../components/ha-control-slider";
import { isUnavailableState } from "../../../data/entity";
import type { HomeAssistant } from "../../../types";
import type { LovelaceCardFeature } from "../types";
import { cardFeatureStyles } from "./common/card-feature-styles";
import type { MediaPlayerVolumeSliderCardFeatureConfig } from "./types";
import { MediaPlayerEntityFeature } from "../../../data/media-player";
import { supportsFeature } from "../../../common/entity/supports-feature";
export const supportsMediaPlayerVolumeSliderCardFeature = (
stateObj: HassEntity
) => {
const domain = computeDomain(stateObj.entity_id);
return (
domain === "media_player" &&
supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET)
);
};
@customElement("hui-media-player-volume-slider-card-feature")
class HuiMediaPlayerVolumeSliderCardFeature
extends LitElement
implements LovelaceCardFeature
{
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ attribute: false }) public stateObj?: HassEntity;
@state() private _config?: MediaPlayerVolumeSliderCardFeatureConfig;
static getStubConfig(): MediaPlayerVolumeSliderCardFeatureConfig {
return {
type: "media-player-volume-slider",
};
}
public setConfig(config: MediaPlayerVolumeSliderCardFeatureConfig): void {
if (!config) {
throw new Error("Invalid configuration");
}
this._config = config;
}
protected render() {
if (
!this._config ||
!this.hass ||
!this.stateObj ||
!supportsMediaPlayerVolumeSliderCardFeature(this.stateObj)
) {
return nothing;
}
const position =
this.stateObj.attributes.volume_level != null
? Math.round(this.stateObj.attributes.volume_level * 100)
: undefined;
return html`
<ha-control-slider
.value=${position}
min="0"
max="100"
.showHandle=${stateActive(this.stateObj)}
.disabled=${!this.stateObj || isUnavailableState(this.stateObj.state)}
@value-changed=${this._valueChanged}
unit="%"
.locale=${this.hass.locale}
></ha-control-slider>
`;
}
private _valueChanged(ev: CustomEvent) {
ev.stopPropagation();
const value = ev.detail.value;
this.hass!.callService("media_player", "volume_set", {
entity_id: this.stateObj!.entity_id,
volume_level: value / 100,
});
}
static get styles() {
return cardFeatureStyles;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-media-player-volume-slider-card-feature": HuiMediaPlayerVolumeSliderCardFeature;
}
}

View File

@@ -34,6 +34,10 @@ export interface LockOpenDoorCardFeatureConfig {
type: "lock-open-door";
}
export interface MediaPlayerVolumeSliderCardFeatureConfig {
type: "media-player-volume-slider";
}
export interface FanPresetModesCardFeatureConfig {
type: "fan-preset-modes";
style?: "dropdown" | "icons";
@@ -161,6 +165,7 @@ export type LovelaceCardFeatureConfig =
| LightColorTempCardFeatureConfig
| LockCommandsCardFeatureConfig
| LockOpenDoorCardFeatureConfig
| MediaPlayerVolumeSliderCardFeatureConfig
| NumericInputCardFeatureConfig
| SelectOptionsCardFeatureConfig
| TargetHumidityCardFeatureConfig

View File

@@ -17,6 +17,7 @@ import "../card-features/hui-light-brightness-card-feature";
import "../card-features/hui-light-color-temp-card-feature";
import "../card-features/hui-lock-commands-card-feature";
import "../card-features/hui-lock-open-door-card-feature";
import "../card-features/hui-media-player-volume-slider-card-feature";
import "../card-features/hui-numeric-input-card-feature";
import "../card-features/hui-select-options-card-feature";
import "../card-features/hui-target-temperature-card-feature";
@@ -51,6 +52,7 @@ const TYPES: Set<LovelaceCardFeatureConfig["type"]> = new Set([
"light-color-temp",
"lock-commands",
"lock-open-door",
"media-player-volume-slider",
"numeric-input",
"select-options",
"target-humidity",

View File

@@ -1,7 +1,7 @@
import type { ActionDetail } from "@material/mwc-list";
import { mdiCheck, mdiDotsVertical } from "@mdi/js";
import { mdiDotsVertical, mdiPlaylistEdit } from "@mdi/js";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
@@ -111,31 +111,10 @@ export class HuiCardLayoutEditor extends LitElement {
</ha-icon-button>
<ha-list-item graphic="icon" .disabled=${!this._uiAvailable}>
${this.hass.localize("ui.panel.lovelace.editor.edit_card.edit_ui")}
${!this._yamlMode
? html`
<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>
`
: nothing}
</ha-list-item>
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.edit_yaml"
`ui.panel.lovelace.editor.edit_view.edit_${!this._yamlMode ? "yaml" : "ui"}`
)}
${this._yamlMode
? html`
<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>
`
: nothing}
<ha-svg-icon slot="graphic" .path=${mdiPlaylistEdit}></ha-svg-icon>
</ha-list-item>
</ha-button-menu>
</div>
@@ -266,10 +245,7 @@ export class HuiCardLayoutEditor extends LitElement {
private async _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._yamlMode = false;
break;
case 1:
this._yamlMode = true;
this._yamlMode = !this._yamlMode;
break;
}
}
@@ -357,9 +333,6 @@ export class HuiCardLayoutEditor extends LitElement {
--mdc-theme-text-primary-on-background: var(--primary-text-color);
margin-top: -8px;
}
.selected_menu_item {
color: var(--primary-color);
}
.disabled {
opacity: 0.5;
pointer-events: none;

View File

@@ -1,5 +1,5 @@
import type { ActionDetail } from "@material/mwc-list";
import { mdiCheck, mdiDelete, mdiDotsVertical, mdiFlask } from "@mdi/js";
import { mdiDelete, mdiDotsVertical, mdiFlask, mdiPlaylistEdit } from "@mdi/js";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
@@ -132,32 +132,12 @@ export class HaCardConditionEditor extends LitElement {
<ha-list-item graphic="icon" .disabled=${!this._uiAvailable}>
${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.edit_ui"
`ui.panel.lovelace.editor.edit_view.edit_${!this._yamlMode ? "yaml" : "ui"}`
)}
${!this._yamlMode
? html`
<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>
`
: ``}
</ha-list-item>
<ha-list-item graphic="icon">
${this.hass.localize(
"ui.panel.lovelace.editor.edit_card.edit_yaml"
)}
${this._yamlMode
? html`
<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>
`
: ``}
<ha-svg-icon
slot="graphic"
.path=${mdiPlaylistEdit}
></ha-svg-icon>
</ha-list-item>
<li divider role="separator"></li>
@@ -239,12 +219,9 @@ export class HaCardConditionEditor extends LitElement {
await this._testCondition();
break;
case 1:
this._yamlMode = false;
this._yamlMode = !this._yamlMode;
break;
case 2:
this._yamlMode = true;
break;
case 3:
this._delete();
break;
}
@@ -325,9 +302,6 @@ export class HaCardConditionEditor extends LitElement {
.content {
padding: 12px;
}
.selected_menu_item {
color: var(--primary-color);
}
.disabled {
opacity: 0.5;
pointer-events: none;

View File

@@ -38,6 +38,7 @@ import { supportsLightBrightnessCardFeature } from "../../card-features/hui-ligh
import { supportsLightColorTempCardFeature } from "../../card-features/hui-light-color-temp-card-feature";
import { supportsLockCommandsCardFeature } from "../../card-features/hui-lock-commands-card-feature";
import { supportsLockOpenDoorCardFeature } from "../../card-features/hui-lock-open-door-card-feature";
import { supportsMediaPlayerVolumeSliderCardFeature } from "../../card-features/hui-media-player-volume-slider-card-feature";
import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature";
import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature";
import { supportsTargetHumidityCardFeature } from "../../card-features/hui-target-humidity-card-feature";
@@ -71,6 +72,7 @@ const UI_FEATURE_TYPES = [
"light-color-temp",
"lock-commands",
"lock-open-door",
"media-player-volume-slider",
"numeric-input",
"select-options",
"target-humidity",
@@ -123,6 +125,7 @@ const SUPPORTS_FEATURE_TYPES: Record<
"light-color-temp": supportsLightColorTempCardFeature,
"lock-commands": supportsLockCommandsCardFeature,
"lock-open-door": supportsLockOpenDoorCardFeature,
"media-player-volume-slider": supportsMediaPlayerVolumeSliderCardFeature,
"numeric-input": supportsNumericInputCardFeature,
"select-options": supportsSelectOptionsCardFeature,
"target-humidity": supportsTargetHumidityCardFeature,

View File

@@ -1,5 +1,5 @@
import type { ActionDetail } from "@material/mwc-list";
import { mdiCheck, mdiClose, mdiDotsVertical } from "@mdi/js";
import { mdiClose, mdiDotsVertical, mdiPlaylistEdit } from "@mdi/js";
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
@@ -157,29 +157,13 @@ export class HuiDialogEditSection
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-list-item graphic="icon">
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_section.edit_ui"
${this.hass.localize(
`ui.panel.lovelace.editor.edit_view.edit_${!this._yamlMode ? "yaml" : "ui"}`
)}
${!this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</ha-list-item>
<ha-list-item graphic="icon">
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_section.edit_yaml"
)}
${this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
<ha-svg-icon
slot="graphic"
.path=${mdiPlaylistEdit}
></ha-svg-icon>
</ha-list-item>
</ha-button-menu>
${!this._yamlMode
@@ -232,10 +216,7 @@ export class HuiDialogEditSection
ev.preventDefault();
switch (ev.detail.index) {
case 0:
this._yamlMode = false;
break;
case 1:
this._yamlMode = true;
this._yamlMode = !this._yamlMode;
break;
}
}
@@ -297,9 +278,6 @@ export class HuiDialogEditSection
text-transform: uppercase;
padding: 0 20px;
}
.selected_menu_item {
color: var(--primary-color);
}
@media all and (min-width: 600px) {
ha-dialog {
--mdc-dialog-min-width: 600px;

View File

@@ -2,7 +2,7 @@ import "@material/mwc-button";
import type { ActionDetail } from "@material/mwc-list";
import "@material/mwc-tab-bar/mwc-tab-bar";
import "@material/mwc-tab/mwc-tab";
import { mdiCheck, mdiClose, mdiDotsVertical } from "@mdi/js";
import { mdiClose, mdiDotsVertical, mdiPlaylistEdit } from "@mdi/js";
import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
@@ -203,31 +203,15 @@ export class HuiDialogEditView extends LitElement {
.label=${this.hass!.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<mwc-list-item graphic="icon">
<ha-list-item graphic="icon">
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.edit_ui"
`ui.panel.lovelace.editor.edit_view.edit_${!this._yamlMode ? "yaml" : "ui"}`
)}
${!this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<mwc-list-item graphic="icon">
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.edit_yaml"
)}
${this._yamlMode
? html`<ha-svg-icon
class="selected_menu_item"
slot="graphic"
.path=${mdiCheck}
></ha-svg-icon>`
: ``}
</mwc-list-item>
<ha-svg-icon
slot="graphic"
.path=${mdiPlaylistEdit}
></ha-svg-icon>
</ha-list-item>
</ha-button-menu>
${convertToSection
? html`
@@ -314,10 +298,7 @@ export class HuiDialogEditView extends LitElement {
ev.preventDefault();
switch (ev.detail.index) {
case 0:
this._yamlMode = false;
break;
case 1:
this._yamlMode = true;
this._yamlMode = !this._yamlMode;
break;
}
}
@@ -551,9 +532,6 @@ export class HuiDialogEditView extends LitElement {
margin-inline-end: auto;
margin-inline-start: initial;
}
.selected_menu_item {
color: var(--primary-color);
}
.hidden {
display: none;
}

View File

@@ -6599,6 +6599,9 @@
"lock-open-door": {
"label": "Lock open door"
},
"media-player-volume-slider": {
"label": "Media player volume slider"
},
"vacuum-commands": {
"label": "Vacuum commands",
"commands": "Commands",

204
yarn.lock
View File

@@ -1606,87 +1606,87 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/ecma402-abstract@npm:2.2.4":
"@formatjs/ecma402-abstract@npm:2.2.5":
version: 2.2.5
resolution: "@formatjs/ecma402-abstract@npm:2.2.5"
dependencies:
"@formatjs/fast-memoize": "npm:2.2.4"
"@formatjs/intl-localematcher": "npm:0.5.8"
tslib: "npm:2"
checksum: 10/1bad17aadc3b91ec4707a20d92aee21ed927cf31eda7260350829b73a78460f84ae38f02e38a13909e8523949b403a68ee250502b16aaf5b38d087069c994f91
languageName: node
linkType: hard
"@formatjs/fast-memoize@npm:2.2.4":
version: 2.2.4
resolution: "@formatjs/ecma402-abstract@npm:2.2.4"
resolution: "@formatjs/fast-memoize@npm:2.2.4"
dependencies:
"@formatjs/fast-memoize": "npm:2.2.3"
tslib: "npm:2"
checksum: 10/1a859b50b9e01bfcb24a643396592ba6fb489ff1cc55f61e4ad33c730d6fb4b393c0eded1c398ba0b85a4f72c6229061946f6a86621f34722e176dc27f9b9e19
languageName: node
linkType: hard
"@formatjs/icu-messageformat-parser@npm:2.9.5":
version: 2.9.5
resolution: "@formatjs/icu-messageformat-parser@npm:2.9.5"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/icu-skeleton-parser": "npm:1.8.9"
tslib: "npm:2"
checksum: 10/dcf618731cb66018ea09a582b1e5c916e1090a4506cab88cf54b55e0540355061283106badf547c1f7b5c56260d88ed631674a072e6ef3900e8f146f5820ef96
languageName: node
linkType: hard
"@formatjs/icu-skeleton-parser@npm:1.8.9":
version: 1.8.9
resolution: "@formatjs/icu-skeleton-parser@npm:1.8.9"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.5"
tslib: "npm:2"
checksum: 10/413f085977cbbab3f06acc7b5304937a33af528015e6b68c76a1b332f7cf4553f081da68992fce2e41f7d2b56ae6641ec712f230833775dd30a2432f56cc9bcf
languageName: node
linkType: hard
"@formatjs/intl-datetimeformat@npm:6.16.6":
version: 6.16.6
resolution: "@formatjs/intl-datetimeformat@npm:6.16.6"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/intl-localematcher": "npm:0.5.8"
tslib: "npm:2"
checksum: 10/f7ab8be1e93f417a4a62c7be645618fec5652d412e55beffeeeada2b74660ad86b1876e3617ef0725f1454b193255dbb37ae779d8bb17ea71643e611e7edab02
checksum: 10/618424ac0607bc1a0b0198f5389d76489e73265ae427601ef4c2769eea1f91d805d09b93631a6a066ca5dc0e19ccc198eb2ea72934884be4df8d66982325df76
languageName: node
linkType: hard
"@formatjs/fast-memoize@npm:2.2.3":
version: 2.2.3
resolution: "@formatjs/fast-memoize@npm:2.2.3"
"@formatjs/intl-displaynames@npm:6.8.6":
version: 6.8.6
resolution: "@formatjs/intl-displaynames@npm:6.8.6"
dependencies:
tslib: "npm:2"
checksum: 10/a9634acb5e03d051e09881eea5484ab02271f7d6b5f96ae9485674ab3c359aa881bc45fc07a1181ae4b2d6e288dadc169f578d142d698913ebbefa373014cac2
languageName: node
linkType: hard
"@formatjs/icu-messageformat-parser@npm:2.9.4":
version: 2.9.4
resolution: "@formatjs/icu-messageformat-parser@npm:2.9.4"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/icu-skeleton-parser": "npm:1.8.8"
tslib: "npm:2"
checksum: 10/f849aa82a00268924d9168c92b588c689b8e130c05e44fad6b41d2892db160de37a02f5af305035e22498db89f595643033b579410a3d9984c95fa0697091de2
languageName: node
linkType: hard
"@formatjs/icu-skeleton-parser@npm:1.8.8":
version: 1.8.8
resolution: "@formatjs/icu-skeleton-parser@npm:1.8.8"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
tslib: "npm:2"
checksum: 10/1fc73406eda84473c39abb141ff02952338dc39288612758ceedc5cdc9798fa7bd990ce6f848f3b5281e686b48395064d8d502b3ced64c3ec8ee67952c9a559b
languageName: node
linkType: hard
"@formatjs/intl-datetimeformat@npm:6.16.5":
version: 6.16.5
resolution: "@formatjs/intl-datetimeformat@npm:6.16.5"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/intl-localematcher": "npm:0.5.8"
tslib: "npm:2"
checksum: 10/c370af6c17d19a0f99162ab9d4365526ae45840aebb54b69081b851f1e2476e97611d31b09a1cf86e56eb41fcbe564c95dab3f922be3a48e4e54c6133568f7c7
checksum: 10/38a68a15f71b897a45e8a1d4b7f078f94e51ea8f72486f1f18f9afb72f8b7180d1028ec870397e17997937d1e518be6ac738a0e266f7218fa4577d846d767204
languageName: node
linkType: hard
"@formatjs/intl-displaynames@npm:6.8.5":
version: 6.8.5
resolution: "@formatjs/intl-displaynames@npm:6.8.5"
"@formatjs/intl-durationformat@npm:0.6.5":
version: 0.6.5
resolution: "@formatjs/intl-durationformat@npm:0.6.5"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/intl-localematcher": "npm:0.5.8"
tslib: "npm:2"
checksum: 10/59a9794096138f98afd6e234d697368f995a9208ff02d9db09a525dbd4c57be9c3aaecdd3d4ec7ac1286f06bc81e681d848a1b476c9466ccb1524f39285e2b8b
checksum: 10/bb513233c8913fd8147ec0432333740e53b39962e0b574c109eb27031121803a29af45dd38906a87f7f03488b9f07f4e37347163083f97e1a58ef11d0cbceda8
languageName: node
linkType: hard
"@formatjs/intl-durationformat@npm:0.6.4":
version: 0.6.4
resolution: "@formatjs/intl-durationformat@npm:0.6.4"
"@formatjs/intl-enumerator@npm:1.8.5":
version: 1.8.5
resolution: "@formatjs/intl-enumerator@npm:1.8.5"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/intl-localematcher": "npm:0.5.8"
"@formatjs/ecma402-abstract": "npm:2.2.5"
tslib: "npm:2"
checksum: 10/aa7c045e94322f0a5723584e79dd1395b3c08e5d1d79432748ae98c0b0c1727059bd1aa5c04090df916ff1aea768336184a2d09ecfd2ece2fb090c472bbd8250
languageName: node
linkType: hard
"@formatjs/intl-enumerator@npm:1.8.4":
version: 1.8.4
resolution: "@formatjs/intl-enumerator@npm:1.8.4"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
tslib: "npm:2"
checksum: 10/fc9b8495bdab13c4c5e69c7a1c85c6444294fd848729e02a78a9af383d306e5c3321a82aa5550e3dda1f429efbb9825dc5646c47b985d9a698c287fcab5c862e
checksum: 10/f8dfe16707420e033b3942a5a5cac8d201db177c5ae57d3283b60ab365efaadd4ddfd4877b0c5d36de42c9310a70f6cb9030cfed86e391c47bdd52f6b77257a3
languageName: node
linkType: hard
@@ -1699,26 +1699,26 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/intl-listformat@npm:7.7.5":
version: 7.7.5
resolution: "@formatjs/intl-listformat@npm:7.7.5"
"@formatjs/intl-listformat@npm:7.7.6":
version: 7.7.6
resolution: "@formatjs/intl-listformat@npm:7.7.6"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/intl-localematcher": "npm:0.5.8"
tslib: "npm:2"
checksum: 10/278f573dde36be2074bee4ac2d4359627f5318473e609a9e68c007a68788fab14bc46d54dc25f43b98a31855997a1bd7460b7fc24add6128a6fd9757b677bda8
checksum: 10/95d659399bdb867477121dba6660599a4e0913599c554893b5925b187c9be9a8129b9008ded24bc5fba3a68b537a456c1b08b14ddea5f057550e6d16ad7f30bf
languageName: node
linkType: hard
"@formatjs/intl-locale@npm:4.2.5":
version: 4.2.5
resolution: "@formatjs/intl-locale@npm:4.2.5"
"@formatjs/intl-locale@npm:4.2.6":
version: 4.2.6
resolution: "@formatjs/intl-locale@npm:4.2.6"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/intl-enumerator": "npm:1.8.4"
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/intl-enumerator": "npm:1.8.5"
"@formatjs/intl-getcanonicallocales": "npm:2.5.3"
tslib: "npm:2"
checksum: 10/6be4c4afeba117095538f193e66055c8ff17c18968a3e4c45e54a579fb0bdd177afa84adba38dbf27725dc3136a44e453ac17edc8e988c63cad61c98be8e7d93
checksum: 10/3d28cf8a00b00cdcf0c029c211cae2f534fed8b735a458bcae83d230e47c1b9ccdfbf2991de77fa54ede329b4e58e48cbaab6b4ee63529c7eb9c0377894f3799
languageName: node
linkType: hard
@@ -1731,36 +1731,36 @@ __metadata:
languageName: node
linkType: hard
"@formatjs/intl-numberformat@npm:8.14.5":
version: 8.14.5
resolution: "@formatjs/intl-numberformat@npm:8.14.5"
"@formatjs/intl-numberformat@npm:8.14.6":
version: 8.14.6
resolution: "@formatjs/intl-numberformat@npm:8.14.6"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/intl-localematcher": "npm:0.5.8"
tslib: "npm:2"
checksum: 10/e613dd1442847d6654b536b53ccc5b0a25c5f41f7b4e89129a1308ee3d5eb5938d4c81b5a3bc08a35bc9aece130caeace1d3c4afb6d0fa02cd68fb9dcc8fcac5
checksum: 10/21edd3601464aed67cf7072b6ea796afd5e06282a2abe5df63189db83c6c7692f8b46caba8b5eb1bf961eb4560c08400c7404f98352c74e8388ebe25ed78633b
languageName: node
linkType: hard
"@formatjs/intl-pluralrules@npm:5.3.5":
version: 5.3.5
resolution: "@formatjs/intl-pluralrules@npm:5.3.5"
"@formatjs/intl-pluralrules@npm:5.3.6":
version: 5.3.6
resolution: "@formatjs/intl-pluralrules@npm:5.3.6"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/intl-localematcher": "npm:0.5.8"
tslib: "npm:2"
checksum: 10/9e9db2be8cd40a9bf05b5cd6b3792c021a1f59d3682faf1b7c0878bd51937d7e9861be2e2ede631c76f23e97c089de82fb6ce0c978a25798da3a51dc8b9ce236
checksum: 10/64cb2bac4ec0741639906aac317f54c10e3bdd29c5f1cc15b5cec48c827229356248437f3c13ecc3a078128a33d36833bc2d6a4dad22093737d67af6e28285df
languageName: node
linkType: hard
"@formatjs/intl-relativetimeformat@npm:11.4.5":
version: 11.4.5
resolution: "@formatjs/intl-relativetimeformat@npm:11.4.5"
"@formatjs/intl-relativetimeformat@npm:11.4.6":
version: 11.4.6
resolution: "@formatjs/intl-relativetimeformat@npm:11.4.6"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/intl-localematcher": "npm:0.5.8"
tslib: "npm:2"
checksum: 10/a1426acad7751f352ab733d0c72ed868b310bd193841fd9aa8f4097b42a6200b243aebf648990fa2fd3fa4755a55184bb3c6801be4d6a14747e55300c74d8144
checksum: 10/d7ca3ecd38ba6c242d31a47aa2f39136df02805a9fac062409dcbf1530bd1e1d365c17d752764a97951cef562325598c8c833e78380ed3a4c88102518b800f23
languageName: node
linkType: hard
@@ -9132,15 +9132,15 @@ __metadata:
"@codemirror/state": "npm:6.4.1"
"@codemirror/view": "npm:6.35.2"
"@egjs/hammerjs": "npm:2.0.17"
"@formatjs/intl-datetimeformat": "npm:6.16.5"
"@formatjs/intl-displaynames": "npm:6.8.5"
"@formatjs/intl-durationformat": "npm:0.6.4"
"@formatjs/intl-datetimeformat": "npm:6.16.6"
"@formatjs/intl-displaynames": "npm:6.8.6"
"@formatjs/intl-durationformat": "npm:0.6.5"
"@formatjs/intl-getcanonicallocales": "npm:2.5.3"
"@formatjs/intl-listformat": "npm:7.7.5"
"@formatjs/intl-locale": "npm:4.2.5"
"@formatjs/intl-numberformat": "npm:8.14.5"
"@formatjs/intl-pluralrules": "npm:5.3.5"
"@formatjs/intl-relativetimeformat": "npm:11.4.5"
"@formatjs/intl-listformat": "npm:7.7.6"
"@formatjs/intl-locale": "npm:4.2.6"
"@formatjs/intl-numberformat": "npm:8.14.6"
"@formatjs/intl-pluralrules": "npm:5.3.6"
"@formatjs/intl-relativetimeformat": "npm:11.4.6"
"@fullcalendar/core": "npm:6.1.15"
"@fullcalendar/daygrid": "npm:6.1.15"
"@fullcalendar/interaction": "npm:6.1.15"
@@ -9264,7 +9264,7 @@ __metadata:
html-minifier-terser: "npm:7.2.0"
husky: "npm:9.1.7"
idb-keyval: "npm:6.2.1"
intl-messageformat: "npm:10.7.7"
intl-messageformat: "npm:10.7.8"
js-yaml: "npm:4.1.0"
jsdom: "npm:25.0.1"
jszip: "npm:3.10.1"
@@ -9679,15 +9679,15 @@ __metadata:
languageName: node
linkType: hard
"intl-messageformat@npm:10.7.7":
version: 10.7.7
resolution: "intl-messageformat@npm:10.7.7"
"intl-messageformat@npm:10.7.8":
version: 10.7.8
resolution: "intl-messageformat@npm:10.7.8"
dependencies:
"@formatjs/ecma402-abstract": "npm:2.2.4"
"@formatjs/fast-memoize": "npm:2.2.3"
"@formatjs/icu-messageformat-parser": "npm:2.9.4"
"@formatjs/ecma402-abstract": "npm:2.2.5"
"@formatjs/fast-memoize": "npm:2.2.4"
"@formatjs/icu-messageformat-parser": "npm:2.9.5"
tslib: "npm:2"
checksum: 10/67e4dba544c4b7a143d1eb57b1d174f9213a648392ecbfdf4648dfaa981b4b6b23d0962ed69ce97a12cc884fc4c4605f31fd8f887ef0fe717d2130c5c8fd8291
checksum: 10/f8a76c2a6898ab4ea6722a28fc603f183212477e036f113b73bd04aa06dae79a86575392b71eb0aa6b17c5161860ee48e8818d00a6c2f5bb4f95b487df8202d0
languageName: node
linkType: hard