diff --git a/ui/app/manager/src/components/configuration/or-conf-json.ts b/ui/app/manager/src/components/configuration/or-conf-json.ts index d4d84296ac..e8eebdc28c 100644 --- a/ui/app/manager/src/components/configuration/or-conf-json.ts +++ b/ui/app/manager/src/components/configuration/or-conf-json.ts @@ -20,11 +20,11 @@ import { html } from "lit"; import { OrElement } from "@openremote/or-element"; import { customElement, property } from "lit/decorators.js"; import "@openremote/or-components/or-ace-editor"; -import type { DialogAction } from "@openremote/or-mwc-components/or-mwc-dialog"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { createRef, type Ref, ref } from "lit/directives/ref.js"; import type { OrAceEditor, OrAceEditorChangedEvent } from "@openremote/or-components/or-ace-editor"; import type { ManagerAppConfig, MapConfig } from "@openremote/model"; +import type { OrVaadinButton } from "@openremote/or-vaadin-components/or-vaadin-button"; @customElement("or-conf-json") export class OrConfJson extends OrElement { @@ -34,6 +34,7 @@ export class OrConfJson extends OrElement { public heading: string; protected _aceEditor: Ref = createRef(); + protected _updateButton: Ref = createRef(); public beforeSave(): false | string | undefined { if (!this._aceEditor.value) { @@ -48,7 +49,7 @@ export class OrConfJson extends OrElement { } protected _showConfigDialog() { - let dialog: OrMwcDialog; + let dialog: OrVaadinDialog | undefined; const _saveConfig = () => { const config = this.beforeSave(); if (config) { @@ -59,55 +60,42 @@ export class OrConfJson extends OrElement { return false; }; - const dialogActions: DialogAction[] = [ - { - actionName: "cancel", - content: "cancel", - }, - { - actionName: "ok", - content: "update", - action: _saveConfig, - }, - ]; - - dialog = new OrMwcDialog() - .setActions(dialogActions) - .setHeading(this.heading) - .setContent(html` - - `) - .setStyles(html` - - `) - .setDismissAction(null); + const onCancel = () => { + dialog?.close(); + }; + const onOk = () => { + if (_saveConfig()) { + dialog?.close(); + } + }; - showDialog(dialog); + dialog = showDialog( + this.shadowRoot!, + html` + +

${this.heading}

+ +
+ + + + + + +
+
+ ` + ); } render() { diff --git a/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-card.ts b/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-card.ts index afc104bd70..c5d645aa93 100644 --- a/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-card.ts +++ b/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-card.ts @@ -23,7 +23,7 @@ import { when } from "lit/directives/when.js"; import "@openremote/or-components/or-file-uploader"; import { i18next } from "@openremote/or-translate"; import type { MapRealmConfig } from "@openremote/model"; -import { type DialogAction, OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { getConfirmDialogContent, showConfirmDialog } from "@openremote/or-vaadin-components/or-vaadin-confirm-dialog"; import type { OrVaadinToggle } from "@openremote/or-vaadin-components/or-vaadin-toggle"; import type { OrMapLongPressEvent } from "@openremote/or-map"; import type { LngLat } from "maplibre-gl"; @@ -57,37 +57,13 @@ export class OrConfMapCard extends OrElement { } protected _showRemoveMapDialog() { - const dialogActions: DialogAction[] = [ - { - actionName: "cancel", - content: "cancel", - }, - { - default: true, - actionName: "ok", - content: "yes", - action: () => { - this.dispatchEvent(new CustomEvent("remove")); - }, - }, - ]; - showDialog( - new OrMwcDialog() - .setHeading(i18next.t("delete")) - .setActions(dialogActions) - .setContent(html` ${i18next.t("configuration.deleteMapCustomizationConfirm")} `) - .setStyles(html` - - `) - .setDismissAction(null) + showConfirmDialog( + this.shadowRoot!, + html` + this.dispatchEvent(new CustomEvent("remove"))}> + ${getConfirmDialogContent("error", "delete", "configuration.deleteMapCustomizationConfirm", "delete", "cancel")} + + ` ); } diff --git a/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-geojson.ts b/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-geojson.ts index ab0f752a86..7528c85a63 100644 --- a/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-geojson.ts +++ b/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-geojson.ts @@ -17,12 +17,14 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import type { GeoJsonConfig } from "@openremote/model"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { html } from "lit"; import { OrElement } from "@openremote/or-element"; import { customElement, property, state } from "lit/decorators.js"; import "@openremote/or-components/or-ace-editor"; -import type { OrAceEditorChangedEvent } from "@openremote/or-components/or-ace-editor"; +import type { OrAceEditor, OrAceEditorChangedEvent } from "@openremote/or-components/or-ace-editor"; +import type { OrVaadinButton } from "@openremote/or-vaadin-components/or-vaadin-button"; +import { createRef, type Ref, ref } from "lit/directives/ref.js"; @customElement("or-conf-map-geojson") export class OrConfMapGeoJson extends OrElement { @@ -32,7 +34,7 @@ export class OrConfMapGeoJson extends OrElement { @state() protected _jsonValid: boolean = true; - protected _dialog: OrMwcDialog; + protected _dialog: OrVaadinDialog; // Value of or-ace-editor without state to prevent UI update protected _aceEditorValue: string; @@ -40,63 +42,50 @@ export class OrConfMapGeoJson extends OrElement { protected render() { return html` - this.showDialog()}> + this._showDialog()}> `; } - protected showDialog() { + protected _showDialog() { + const buttonRef: Ref = createRef(); + const editorRef: Ref = createRef(); + const onOk = () => { + this.geoJson = this.parseGeoJson(this._aceEditorValue); // update with new value + this.dispatchEvent(new CustomEvent("update", { detail: { value: this.geoJson } })); + this._dialog?.close(); + }; this._dialog = showDialog( - new OrMwcDialog() - .setHeading("GeoJSON editor") - .setStyles( - html` ` - ) - .setActions([ - { - actionName: "close", - content: "close", - }, - { - actionName: "ok", - content: "update", - disabled: !this._jsonValid, - action: () => { - this.geoJson = this.parseGeoJson(this._aceEditorValue); // update with new value - this.dispatchEvent(new CustomEvent("update", { detail: { value: this.geoJson } })); - }, - }, - ]) - .setContent( - () => - html` ` - ) - .setDismissAction(null) + this.shadowRoot!, + html` + +

+ GeoJSON editor +

+ +
+ this._dialog?.close()}> + + + + + +
+
+ ` ); } diff --git a/ui/app/manager/src/components/configuration/or-conf-panel.ts b/ui/app/manager/src/components/configuration/or-conf-panel.ts index b58825665d..238fd6fcc6 100644 --- a/ui/app/manager/src/components/configuration/or-conf-panel.ts +++ b/ui/app/manager/src/components/configuration/or-conf-panel.ts @@ -18,18 +18,18 @@ */ import { html, type TemplateResult } from "lit"; import { OrElement } from "@openremote/or-element"; -import { InputType, OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input"; import { customElement, property } from "lit/decorators.js"; import { when } from "lit/directives/when.js"; import type { ManagerAppConfig, MapConfig } from "@openremote/model"; -import { type DialogAction, OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; -import { i18next } from "@openremote/or-translate"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; +import type { OrVaadinButton } from "@openremote/or-vaadin-components/or-vaadin-button"; import "@openremote/or-components/or-loading-indicator"; import "./or-conf-map/or-conf-map-card"; import "./or-conf-realm/or-conf-realm-card"; import type { OrConfRealmCard } from "./or-conf-realm/or-conf-realm-card"; import type { OrConfMapCard } from "./or-conf-map/or-conf-map-card"; import type { OrVaadinSelect } from "@openremote/or-vaadin-components/or-vaadin-select"; +import { createRef, ref, type Ref } from "lit/directives/ref.js"; @customElement("or-conf-panel") export class OrConfPanel extends OrElement { @@ -184,39 +184,30 @@ export class OrConfPanel extends OrElement { // Show the dialog to "add realm", that allows users to override realm options. protected _showAddingRealmDialog() { this._addedRealm = null; - const dialogActions: DialogAction[] = [ - { - actionName: "cancel", - content: "cancel", - }, - { - default: true, - actionName: "ok", - content: "ok", - action: () => { - if (this._addedRealm) { - let realms = this.getRealmsProperty(this.config); - if (!realms) { - realms = {}; - } - if (this.isManagerConfig(this.config)) { - realms[this._addedRealm] = {}; // empty object since no fields are required - } else if (this.isMapConfig(this.config)) { - realms[this._addedRealm] = { - bounds: [4.42, 51.88, 4.55, 51.94], - center: [4.485222, 51.911712], - zoom: 14, - minZoom: 14, - maxZoom: 19, - boxZoom: false, - }; - } - this.requestUpdate("config"); - this.notifyConfigChange(this.config); - } - }, - }, - ]; + let dialog: OrVaadinDialog | undefined; + const onOk = () => { + if (this._addedRealm) { + let realms = this.getRealmsProperty(this.config); + if (!realms) { + realms = {}; + } + if (this.isManagerConfig(this.config)) { + realms[this._addedRealm] = {}; // empty object since no fields are required + } else if (this.isMapConfig(this.config)) { + realms[this._addedRealm] = { + bounds: [4.42, 51.88, 4.55, 51.94], + center: [4.485222, 51.911712], + zoom: 14, + minZoom: 14, + maxZoom: 19, + boxZoom: false, + }; + } + this.requestUpdate("config"); + this.notifyConfigChange(this.config); + dialog?.close(); + } + }; const headingKey = this.isMapConfig(this.config) ? "configuration.addMapCustomization" : "configuration.addRealmCustomization"; @@ -224,42 +215,38 @@ export class OrConfPanel extends OrElement { value: item.name, label: item.displayName, })); - showDialog( - new OrMwcDialog() - .setHeading(i18next.t(headingKey)) - .setActions(dialogActions) - .setContent(html` -
- (this._addedRealm = (ev.currentTarget as OrVaadinSelect).value)} - > - - -
- `) - .setStyles(html` - - `) - .setDismissAction(null) + const selectRef: Ref = createRef(); + const buttonRef: Ref = createRef(); + dialog = showDialog( + this.shadowRoot!, + html` + +

+ +

+ { + this._addedRealm = (ev.currentTarget as OrVaadinSelect).value; + buttonRef.value!.disabled = !selectRef.value?.checkValidity(); + }} + > + + +
+ dialog?.close()}> + + + + + +
+
+ ` ); } } diff --git a/ui/app/manager/src/components/configuration/or-conf-realm/or-conf-realm-card.ts b/ui/app/manager/src/components/configuration/or-conf-realm/or-conf-realm-card.ts index 52daf63539..3486bccdf5 100644 --- a/ui/app/manager/src/components/configuration/or-conf-realm/or-conf-realm-card.ts +++ b/ui/app/manager/src/components/configuration/or-conf-realm/or-conf-realm-card.ts @@ -35,10 +35,10 @@ import { } from "@openremote/core"; import { i18next, translate } from "@openremote/or-translate"; import type { FileInfo, ManagerAppRealmConfig } from "@openremote/model"; -import { type DialogAction, OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; import { when } from "lit/directives/when.js"; import { DefaultHeaderMainMenu, DefaultHeaderSecondaryMenu } from "../../../index"; import type { OrVaadinMultiSelectComboBox } from "@openremote/or-vaadin-components/or-vaadin-multi-select-combo-box"; +import { getConfirmDialogContent, showConfirmDialog } from "@openremote/or-vaadin-components/or-vaadin-confirm-dialog"; @customElement("or-conf-realm-card") export class OrConfRealmCard extends translate(i18next)(OrElement) { @@ -229,37 +229,13 @@ export class OrConfRealmCard extends translate(i18next)(OrElement) { } protected _showRemoveRealmDialog() { - const dialogActions: DialogAction[] = [ - { - actionName: "cancel", - content: "cancel", - }, - { - default: true, - actionName: "ok", - content: "yes", - action: () => { - this.dispatchEvent(new CustomEvent("remove")); - }, - }, - ]; - showDialog( - new OrMwcDialog() - .setHeading(i18next.t("delete")) - .setActions(dialogActions) - .setContent(html` ${i18next.t("configuration.deleteRealmCustomizationConfirm")} `) - .setStyles(html` - - `) - .setDismissAction(null) + showConfirmDialog( + this.shadowRoot!, + html` + this.dispatchEvent(new CustomEvent("remove"))}> + ${getConfirmDialogContent("error", "delete", "configuration.deleteRealmCustomizationConfirm", "delete", "cancel")} + + ` ); } diff --git a/ui/app/manager/src/pages/page-assets.ts b/ui/app/manager/src/pages/page-assets.ts index 39ef7679af..fa922e8223 100644 --- a/ui/app/manager/src/pages/page-assets.ts +++ b/ui/app/manager/src/pages/page-assets.ts @@ -43,16 +43,16 @@ import { OrAssetTreeRequestSelectionEvent, OrAssetTreeSelectionEvent, } from "@openremote/or-asset-tree"; -import manager, { DefaultBoxShadow, DefaultColor5, Util } from "@openremote/core"; +import manager, { DefaultBoxShadow, Util } from "@openremote/core"; import { type AppStateKeyed, Page, type PageProvider, router } from "@openremote/or-app"; import { createSlice, type Store, createSelector } from "@reduxjs/toolkit"; -import { type DialogAction, OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; -import { i18next } from "@openremote/or-translate"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { type Asset, AssetEventCause, WellknownAssets } from "@openremote/model"; import "@openremote/or-json-forms"; import { getAlarmsRoute, getAssetsRoute, getUsersRoute } from "../routes"; import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar"; import { getConfirmDialogContent, showConfirmDialog } from "@openremote/or-vaadin-components/or-vaadin-confirm-dialog"; +import { createRef, ref, type Ref } from "lit/directives/ref.js"; export interface PageAssetsConfig { viewer?: ViewerConfig; @@ -274,82 +274,59 @@ export class PageAssets extends Page { } protected _onParentChangeClick() { - let dialog: OrMwcDialog; + let dialog: OrVaadinDialog | undefined; + const assetTreeRef: Ref = createRef(); - const blockEvent = (ev: Event) => { - ev.stopPropagation(); - }; - - const dialogContent = html` `; + const blockEvent = (ev: Event) => ev.stopPropagation(); const setParent = () => { - const assetTree = dialog.shadowRoot!.getElementById("parent-asset-tree") as OrAssetTree; + const assetTree = assetTreeRef.value!; const idd = assetTree.selectedIds!.length === 1 ? assetTree.selectedIds![0] : undefined; this._onAssetParentChange({ parentId: idd, assetIds: this._assetIds }); }; const clearParent = () => { this._onAssetParentChange({ parentId: undefined, assetIds: this._assetIds }); + dialog?.close(); }; - const dialogActions: DialogAction[] = [ - { - actionName: "clear", - content: "none", - action: clearParent, - }, - { - actionName: "ok", - content: "ok", - action: setParent, - }, - { - default: true, - actionName: "cancel", - content: "cancel", - }, - ]; + const onCancel = () => dialog?.close(); + const onOk = () => { + setParent(); + dialog?.close(); + }; dialog = showDialog( - new OrMwcDialog() - .setContent(dialogContent) - .setActions(dialogActions) - .setStyles(html` - - `) - .setHeading(i18next.t("setParent")) - .setDismissAction(null) + this.shadowRoot!, + html` + +

+ +

+ +
+ + + +
+ + + + + + +
+
+
+ ` ); } diff --git a/ui/app/manager/src/pages/page-gateway-tunnel.ts b/ui/app/manager/src/pages/page-gateway-tunnel.ts index 8066a8d471..071e609318 100644 --- a/ui/app/manager/src/pages/page-gateway-tunnel.ts +++ b/ui/app/manager/src/pages/page-gateway-tunnel.ts @@ -28,12 +28,14 @@ import { DefaultColor3, manager } from "@openremote/core"; import { type Asset, type AssetQuery, type GatewayTunnelInfo, GatewayTunnelInfoType } from "@openremote/model"; import type { TableColumn, TableRow } from "@openremote/or-mwc-components/or-mwc-table"; import { getAssetsRoute } from "../routes"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar"; import moment from "moment"; import type { OrVaadinComboBox } from "@openremote/or-vaadin-components/or-vaadin-combo-box"; import type { OrVaadinSelect } from "@openremote/or-vaadin-components/or-vaadin-select"; import { getConfirmDialogContent, showConfirmDialog } from "@openremote/or-vaadin-components/or-vaadin-confirm-dialog"; +import type { OrVaadinButton } from "@openremote/or-vaadin-components/or-vaadin-button"; +import { createRef, ref, type Ref } from "lit/directives/ref.js"; export function pageGatewayTunnelProvider(store: Store): PageProvider { return { @@ -313,25 +315,21 @@ export class PageGatewayTunnel extends Page { */ protected _onAddTunnelClick() { const tunnel = {} as GatewayTunnelInfo; - let dialog: OrMwcDialog | undefined; + let dialog: OrVaadinDialog | undefined; + const buttonRef: Ref = createRef(); const onAddClick = () => { this._tryStartTunnel(tunnel); + dialog?.close(); }; - const updateActions = () => { - dialog.setActions([ - { actionName: "cancel", content: "cancel" }, - { - actionName: "add", - disabled: - !tunnel.gatewayId || - (tunnel.hostname && tunnel.hostname.trim().length === 0) || - (tunnel.targetPort !== undefined && (tunnel.targetPort <= 0 || tunnel.targetPort > 65535)), - content: "add", - action: () => onAddClick(), - }, - ]); + + const checkValidity = () => { + buttonRef.value!.disabled = + !tunnel.gatewayId || + (tunnel.hostname && tunnel.hostname.trim().length === 0) || + (tunnel.targetPort !== undefined && (tunnel.targetPort <= 0 || tunnel.targetPort > 65535)); }; + const gatewayListTemplate = async (): Promise => { const gatewayAssets = await this._fetchGatewayAssets(); const items: { value: any; label: string }[] = gatewayAssets.map((g) => ({ value: g.id, label: g.name })); @@ -340,13 +338,13 @@ export class PageGatewayTunnel extends Page { label: String(g), })); return html` -
+
{ tunnel.gatewayId = (ev.currentTarget as OrVaadinComboBox).value; - updateActions(); + checkValidity(); }} > @@ -357,7 +355,7 @@ export class PageGatewayTunnel extends Page { placeholder=${this.GATEWAY_TUNNEL_PROTOCOL_TYPES?.[0]} @change=${(ev: Event) => { tunnel.type = (ev.currentTarget as OrVaadinSelect).value as GatewayTunnelInfoType; - updateActions(); + checkValidity(); }} > @@ -367,7 +365,7 @@ export class PageGatewayTunnel extends Page { placeholder="localhost" @change=${(ev: Event) => { tunnel.target = (ev.currentTarget as HTMLInputElement).value; - updateActions(); + checkValidity(); }} > @@ -380,7 +378,7 @@ export class PageGatewayTunnel extends Page { @change=${(ev: Event) => { const value = (ev.currentTarget as HTMLInputElement).value; tunnel.targetPort = value ? Number(value) : undefined; - updateActions(); + checkValidity(); }} > @@ -388,12 +386,26 @@ export class PageGatewayTunnel extends Page {
`; }; - dialog = new OrMwcDialog() - .setHeading(`${i18next.t("add")} ${i18next.t("tunnel")}`) - .setContent(html` ${until(gatewayListTemplate(), html`${i18next.t("loading")}`)} `); - - updateActions(); - showDialog(dialog); + dialog = showDialog( + this.shadowRoot!, + html` + +

+ + +

+ ${until(gatewayListTemplate(), html``)} +
+ dialog?.close()}> + + + onAddClick()}> + + +
+
+ ` + ); } /** diff --git a/ui/app/manager/src/pages/page-gateway.ts b/ui/app/manager/src/pages/page-gateway.ts index 99111b4c72..cdc945ff43 100644 --- a/ui/app/manager/src/pages/page-gateway.ts +++ b/ui/app/manager/src/pages/page-gateway.ts @@ -23,7 +23,8 @@ import { when } from "lit/directives/when.js"; import { until } from "lit/directives/until.js"; import { createRef, type Ref, ref } from "lit/directives/ref.js"; import "@openremote/or-components/or-panel"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog as showVaadinDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { type AttributeDescriptor, type AttributePredicate, @@ -35,7 +36,7 @@ import { LogicGroupOperator, type GatewayAssetSyncRule, } from "@openremote/model"; -import manager, { DefaultColor1, DefaultColor3 } from "@openremote/core"; +import manager, { DefaultColor1 } from "@openremote/core"; import { InputType, type OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input"; import type { OrVaadinToggle } from "@openremote/or-vaadin-components/or-vaadin-toggle"; import { type AppStateKeyed, Page, type PageProvider } from "@openremote/or-app"; @@ -774,49 +775,57 @@ export class PageGateway extends Page { * The changed are only applied once the save button is pressed. */ protected _openConnectionJSONEditor(connection?: GatewayConnection) { + let dialog: OrVaadinDialog | undefined; const editorRef: Ref = createRef(); - showDialog( - new OrMwcDialog() - .setHeading("JSON Editor") - .setContent(html` + const onCancel = () => dialog?.close(); + const onOk = () => { + const editor = editorRef.value; + if (!editor.validate()) { + console.warn("JSON was not valid"); + showSnackbar(undefined, i18next.t("errorOccurred")); + return; + } + try { + let parsed: GatewayAttributeFilter[] | undefined; + if (editor.getValue().length > 0) { + parsed = JSON.parse(editor.getValue()); + + // Verify if the JSON is an array. If so; simply accept the format. + if (!Array.isArray(parsed)) { + console.warn("Could not parse JSON to GatewayAttributeFilter[], as it was not an array."); + showSnackbar(undefined, i18next.t("errorOccurred")); + return; + } + } + this._updateAttributeFilters(parsed); + dialog?.close(); + } catch (e) { + console.error(e); + showSnackbar(undefined, i18next.t("errorOccurred")); + } + }; + dialog = showVaadinDialog( + this.shadowRoot!, + html` + +

+ JSON Editor +

- `) - .setActions([ - { actionName: "cancel", content: "cancel" }, - { - actionName: "save", - content: "save", - action: () => { - const editor = editorRef.value; - if (!editor.validate()) { - console.warn("JSON was not valid"); - showSnackbar(undefined, i18next.t("errorOccurred")); - return; - } - try { - let parsed: GatewayAttributeFilter[] | undefined; - if (editor.getValue().length > 0) { - parsed = JSON.parse(editor.getValue()); - - // Verify if the JSON is an array. If so; simply accept the format. - if (!Array.isArray(parsed)) { - console.warn("Could not parse JSON to GatewayAttributeFilter[], as it was not an array."); - showSnackbar(undefined, i18next.t("errorOccurred")); - return; - } - } - this._updateAttributeFilters(parsed); - } catch (e) { - console.error(e); - showSnackbar(undefined, i18next.t("errorOccurred")); - } - }, - }, - ]) +
+ + + + + + +
+
+ ` ); } } diff --git a/ui/app/manager/src/pages/page-realms.ts b/ui/app/manager/src/pages/page-realms.ts index f5c02710cf..571089f352 100644 --- a/ui/app/manager/src/pages/page-realms.ts +++ b/ui/app/manager/src/pages/page-realms.ts @@ -28,8 +28,11 @@ import { type AppStateKeyed, Page, type PageProvider } from "@openremote/or-app" import { ClientRole, type Realm } from "@openremote/model"; import { i18next } from "@openremote/or-translate"; import type { OrIcon } from "@openremote/or-icon"; -import { InputType } from "@openremote/or-mwc-components/or-mwc-input"; -import { type DialogAction, OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { + getConfirmDialogContent, + type OrVaadinConfirmDialog, + showConfirmDialog, +} from "@openremote/or-vaadin-components/or-vaadin-confirm-dialog"; import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar"; import type { OrVaadinTextField } from "@openremote/or-vaadin-components/or-vaadin-text-field"; import type { OrVaadinButton } from "@openremote/or-vaadin-components/or-vaadin-button"; @@ -522,8 +525,8 @@ export class PageRealms extends Page { let confirmedName = ""; const okBtnRef: Ref = createRef(); - const doDelete = async (dialog: OrMwcDialog) => { - if (okBtnRef.value.disabled) return; + const doDelete = async (dialog?: OrVaadinConfirmDialog) => { + if (okBtnRef.value!.disabled) return; try { await manager.rest.api.RealmResource.delete(realm.name); this._realms = this._realms.filter((r) => r !== realm); @@ -531,6 +534,8 @@ export class PageRealms extends Page { } catch (e) { const snackbarKey = (e as any)?.response?.status === 409 ? "realmDeleteFailedHasAssets" : "realmDeleteFailed"; showSnackbar(undefined, snackbarKey, "dismiss"); + } finally { + dialog?.close(); } }; @@ -550,45 +555,23 @@ export class PageRealms extends Page {
`; - const dialogActions: DialogAction[] = [ - { - actionName: "ok", - content: html``, - action: doDelete, - }, - { - default: true, - actionName: "cancel", - content: "cancel", - }, - ]; - - const dialog = showDialog( - new OrMwcDialog() - .setContent(dialogContent) - .setActions(dialogActions) - .setStyles(html` - - `) - .setDismissAction(null) + const dialog = showConfirmDialog( + this.shadowRoot!, + html` + doDelete(dialog)}> + ${getConfirmDialogContent( + "error", + "deleteRealmConfirm", + dialogContent, + html` + + + + `, + "cancel" + )} + + ` ); } diff --git a/ui/app/manager/src/pages/page-users.ts b/ui/app/manager/src/pages/page-users.ts index 59eaefbee2..afe58e55b8 100644 --- a/ui/app/manager/src/pages/page-users.ts +++ b/ui/app/manager/src/pages/page-users.ts @@ -33,7 +33,7 @@ import { type UserSession, } from "@openremote/model"; import { i18next } from "@openremote/or-translate"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar"; import { type GenericAxiosResponse, isAxiosError } from "@openremote/rest"; import type { OrAssetTreeRequestSelectionEvent, OrAssetTreeSelectionEvent } from "@openremote/or-asset-tree"; @@ -826,10 +826,27 @@ export class PageUsers extends Page { }); }; - const dialog = showDialog( - new OrMwcDialog() - .setHeading(i18next.t("linkedAssets")) - .setContent(html` + let dialog: OrVaadinDialog | undefined; + const onCancel = () => { + user.userAssetLinks = user.previousAssetLinks; + user.previousAssetLinks = undefined; + openBtn.disabled = false; + dialog?.close(); + }; + const onOk = () => { + openBtn.disabled = false; + this.onUserChanged(suffix); + this.requestUpdate(); + dialog?.close(); + }; + + dialog = showDialog( + this.shadowRoot!, + html` + +

+ +

{ } }}" > - `) - .setActions([ - { - default: true, - actionName: "cancel", - content: "cancel", - action: () => { - user.userAssetLinks = user.previousAssetLinks; - user.previousAssetLinks = undefined; - openBtn.disabled = false; - }, - }, - { - actionName: "ok", - content: "ok", - action: () => { - openBtn.disabled = false; - this.onUserChanged(suffix); - this.requestUpdate(); - }, - }, - ]) - .setDismissAction({ - actionName: "cancel", - action: () => { - user.userAssetLinks = user.previousAssetLinks; - user.previousAssetLinks = undefined; - openBtn.disabled = false; - }, - }) +
+ + + + + + +
+
+ ` ); } diff --git a/ui/app/manager/test/assets.test.ts b/ui/app/manager/test/assets.test.ts index 16d4bab650..4dd762019d 100644 --- a/ui/app/manager/test/assets.test.ts +++ b/ui/app/manager/test/assets.test.ts @@ -195,8 +195,8 @@ test.describe("Parent asset", () => { await assetViewer.switchMode("modify"); await page.getByText("Parent Edit").getByRole("button").click(); - await page.getByLabel("Select parent asset").getByText("Parent").click(); - await page.getByLabel("Select parent asset").getByRole("button", { name: "OK" }).click(); + await page.getByRole("dialog").getByText("Parent").click(); + await page.getByRole("dialog").getByRole("button", { name: "OK" }).click(); await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByRole("textbox", { name: "Parent" })).toHaveValue("Parent"); @@ -217,7 +217,7 @@ test.describe("Parent asset", () => { await assetViewer.switchMode("modify"); await page.getByText("Parent Edit").getByRole("button").click(); - await page.getByLabel("Select parent asset").getByRole("button", { name: "NONE" }).click(); + await page.getByRole("dialog").getByRole("button", { name: "Clear" }).click(); await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByRole("textbox", { name: "Parent" })).toBeEmpty(); @@ -243,7 +243,7 @@ test.describe("Attributes", () => { await page.getByRole("button", { name: "Add attribute" }).click(); - const dialog = page.getByLabel("Add attribute"); + const dialog = page.getByRole("dialog"); await dialog.getByLabel("Name").fill("test"); await dialog.getByRole("combobox", { name: "Value type", exact: true }).fill("Int"); await dialog.getByRole("option", { name: "Integer", exact: true }).click(); diff --git a/ui/app/manager/test/fixtures/pages/assets.ts b/ui/app/manager/test/fixtures/pages/assets.ts index bad4004e3e..5f01459e1d 100644 --- a/ui/app/manager/test/fixtures/pages/assets.ts +++ b/ui/app/manager/test/fixtures/pages/assets.ts @@ -50,7 +50,7 @@ export class AssetsPage implements BasePage { await this.shared.interceptResponse("**/asset", (asset) => { if (asset) this.manager.assets.push(asset); }); - await this.page.click("#add-btn"); + await this.page.getByRole("button", { name: "Add" }).click(); } /** diff --git a/ui/app/manager/test/fixtures/pages/realms.ts b/ui/app/manager/test/fixtures/pages/realms.ts index e4a726d191..291dc98f3a 100644 --- a/ui/app/manager/test/fixtures/pages/realms.ts +++ b/ui/app/manager/test/fixtures/pages/realms.ts @@ -60,6 +60,6 @@ export class RealmsPage implements BasePage { await this.page.getByRole("cell", { name: realm }).first().click(); await this.page.getByRole("button", { name: "Delete" }).click(); await this.page.getByRole("alertdialog").getByRole("textbox", { name: "Realm" }).fill(realm); - await this.page.getByRole("button", { name: "OK" }).click(); + await this.page.getByRole("alertdialog").getByRole("button", { name: "Delete" }).click(); } } diff --git a/ui/component/or-asset-tree/src/index.ts b/ui/component/or-asset-tree/src/index.ts index 102499012a..b6e166f055 100644 --- a/ui/component/or-asset-tree/src/index.ts +++ b/ui/component/or-asset-tree/src/index.ts @@ -64,12 +64,12 @@ import { getAssetDescriptorIconTemplate, type OrIcon } from "@openremote/or-icon import type { ListItem } from "@openremote/or-mwc-components/or-mwc-list"; import "@openremote/or-mwc-components/or-mwc-list"; import { i18next } from "@openremote/or-translate"; -import "@openremote/or-mwc-components/or-mwc-dialog"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import type { OrAddAssetDialog, OrAddChangedEvent } from "./or-add-asset-dialog"; import "./or-add-asset-dialog"; import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar"; import { when } from "lit/directives/when.js"; +import { createRef, ref, type Ref } from "lit/directives/ref.js"; import debounce from "lodash.debounce"; export interface AssetTreeTypeConfig { @@ -1564,108 +1564,102 @@ export class OrAssetTree extends subscribe(manager)(OrElement) { const agentTypes = types.filter((t) => t.descriptorType === "agent"); const assetTypes = types.filter((t) => t.descriptorType === "asset"); const parent = this._selectedNodes && this._selectedNodes.length === 1 ? this._selectedNodes[0].asset : undefined; - let dialog: OrMwcDialog; + let dialog: OrVaadinDialog | undefined; + const addAssetDialogRef: Ref = createRef(); + const addBtnRef: Ref = createRef(); const onAddChanged = (ev: OrAddChangedEvent) => { const nameValid = !!ev.detail.name && ev.detail.name.trim().length > 0 && ev.detail.name.trim().length < 1024; - const addBtn = dialog.shadowRoot!.getElementById("add-btn") as OrVaadinButton; + const addBtn = addBtnRef.value!; addBtn.disabled = !ev.detail.descriptor || !nameValid; }; + const onCancel = () => dialog?.close(); + const onOk = () => { + const addAssetDialog = addAssetDialogRef.value!; + const descriptor = addAssetDialog.selectedType; + const selectedOptionalAttributes = addAssetDialog.selectedAttributes; + const name = addAssetDialog.name.trim(); + const parent = addAssetDialog.parent; + + if (!descriptor) { + return; + } + + const asset: Asset = { + name, + type: descriptor.name, + realm: manager.displayRealm, + }; + + // Construct attributes + const assetTypeInfo = AssetModelUtil.getAssetTypeInfo(descriptor.name!); + + if (!assetTypeInfo) { + return; + } + + if (assetTypeInfo.attributeDescriptors) { + asset.attributes = {}; + assetTypeInfo.attributeDescriptors + .filter((attributeDescriptor) => !attributeDescriptor.optional) + .forEach((attributeDescriptor) => { + asset.attributes![attributeDescriptor.name!] = { + name: attributeDescriptor.name, + type: attributeDescriptor.type, + meta: attributeDescriptor.meta ? { ...attributeDescriptor.meta } : undefined, + } as Attribute; + }); + } + + if (selectedOptionalAttributes) { + selectedOptionalAttributes?.forEach((attribute) => { + asset.attributes![attribute.name!] = { + name: attribute.name, + type: attribute.type, + meta: attribute.meta ? { ...attribute.meta } : undefined, + }; + }); + } + + if (this.selectedIds) { + asset.parentId = parent ? parent.id : undefined; + } + const detail: AddEventDetail = { + asset, + }; + dialog?.close(); + Util.dispatchCancellableEvent(this, new OrAssetTreeRequestAddEvent(detail)).then((detail) => { + if (detail.allow) { + this.dispatchEvent(new OrAssetTreeAddEvent(detail.detail)); + } + }); + }; dialog = showDialog( - new OrMwcDialog() - .setHeading(i18next.t("addAsset")) - .setContent(html` + this.shadowRoot!, + html` + +

+ +

- `) - .setActions([ - { - actionName: "cancel", - content: html``, - }, - { - actionName: "add", - content: html` +
+ + + + - `, - action: () => { - const addAssetDialog = dialog.shadowRoot!.getElementById("add-panel") as OrAddAssetDialog; - const descriptor = addAssetDialog.selectedType; - const selectedOptionalAttributes = addAssetDialog.selectedAttributes; - const name = addAssetDialog.name.trim(); - const parent = addAssetDialog.parent; - - if (!descriptor) { - return; - } - - const asset: Asset = { - name, - type: descriptor.name, - realm: manager.displayRealm, - }; - - // Construct attributes - const assetTypeInfo = AssetModelUtil.getAssetTypeInfo(descriptor.name!); - - if (!assetTypeInfo) { - return; - } - - if (assetTypeInfo.attributeDescriptors) { - asset.attributes = {}; - assetTypeInfo.attributeDescriptors - .filter((attributeDescriptor) => !attributeDescriptor.optional) - .forEach((attributeDescriptor) => { - asset.attributes![attributeDescriptor.name!] = { - name: attributeDescriptor.name, - type: attributeDescriptor.type, - meta: attributeDescriptor.meta ? { ...attributeDescriptor.meta } : undefined, - } as Attribute; - }); - } - - if (selectedOptionalAttributes) { - selectedOptionalAttributes?.forEach((attribute) => { - asset.attributes![attribute.name!] = { - name: attribute.name, - type: attribute.type, - meta: attribute.meta ? { ...attribute.meta } : undefined, - }; - }); - } - - if (this.selectedIds) { - asset.parentId = parent ? parent.id : undefined; - } - const detail: AddEventDetail = { - asset, - }; - Util.dispatchCancellableEvent(this, new OrAssetTreeRequestAddEvent(detail)).then((detail) => { - if (detail.allow) { - this.dispatchEvent(new OrAssetTreeAddEvent(detail.detail)); - } - }); - }, - }, - ]) - .setStyles(html` - - `) - .setDismissAction(null) + +
+
+ ` ); } diff --git a/ui/component/or-asset-tree/src/or-add-asset-dialog.ts b/ui/component/or-asset-tree/src/or-add-asset-dialog.ts index 29c2417382..8de2324284 100644 --- a/ui/component/or-asset-tree/src/or-add-asset-dialog.ts +++ b/ui/component/or-asset-tree/src/or-add-asset-dialog.ts @@ -108,119 +108,136 @@ export class OrAddAssetDialog extends OrElement { public static get styles() { // language=CSS return css` - :host{ - border-style: solid; - border-color: var(--or-app-color5, ${unsafeCSS(DefaultColor5)}); - border-width: 1px 0; - } - - #name-wrapper { - display: flex; - flex-direction: column; - margin-top: 12px; - } - - #toggle-parent-selector, - #remove-parent { - margin: 4px 0 0 5px; - } - - #name-input, - #parent-wrapper { - margin: 10px 0; - } - - #parent-wrapper { - display: flex; - align-items: end; - } - - #parent { - flex: 1 1 auto; - } - - #parent-selector { - max-width: 250px; - border-left: 1px solid var(--or-app-color5, ${unsafeCSS(DefaultColor5)}); - } - - #mdc-dialog-form-add { - display: flex; - height: 600px; - max-height: 600px; - width: 1000px; - } - - .msg { - display: flex; - justify-content: center; - align-items: center; - text-align: center; - height: 100%; - font-family: "Segoe UI", Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; - font-size: 14px; - } - - #asset-type-option-container { - padding: 15px; - flex: 1 1 auto; - overflow: auto; - max-width: 100%; - font-size: 16px; - } - - #type-list-filter { - padding: 7px; - background-color: var(--or-app-color2, ${unsafeCSS(DefaultColor2)})) - } - - #filterInput{ - width: 100%; - } - - #type-list { - overflow: auto; - text-transform: capitalize; - } - - #type-list-container { - display: flex; - flex-direction: column; - width: 300px; - border-right: 1px solid var(--or-app-color5, ${unsafeCSS(DefaultColor5)}); - } - - #type-title { - display: flex; - align-items: center; - margin: 9px 4px; - } - - #type-description { - text-transform: capitalize; - color: var(--or-app-color3, ${unsafeCSS(DefaultColor3)}); - margin-left: 10px; - font-size: 18px; - font-weight: bold; - font-family: "Segoe UI", Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; - } - - .heading, - .mdc-list-group__subheader { - text-transform: uppercase; - font-family: "Segoe UI", Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; - font-weight: bolder; - line-height: 1em; - color: var(--or-app-color3, ${unsafeCSS(DefaultColor3)}); - letter-spacing: 0.025em; - font-size: 14px; - margin: 20px 0 10px; - } - - .mdc-list-group__subheader { - margin: 20px 0 0 16px; - } - `; + :host { + border-style: solid; + border-color: var(--or-app-color5, ${unsafeCSS(DefaultColor5)}); + border-width: 1px 0; + } + + #name-wrapper { + display: flex; + flex-direction: column; + margin-top: 12px; + } + + #toggle-parent-selector, + #remove-parent { + margin: 4px 0 0 5px; + } + + #name-input, + #parent-wrapper { + margin: 10px 0; + } + + #parent-wrapper { + display: flex; + align-items: end; + } + + #parent { + flex: 1 1 auto; + } + + #parent-selector { + max-width: 250px; + border-left: 1px solid var(--or-app-color5, ${unsafeCSS(DefaultColor5)}); + } + + #mdc-dialog-form-add { + display: flex; + height: 600px; + max-height: 600px; + width: 1000px; + } + + .msg { + display: flex; + justify-content: center; + align-items: center; + text-align: center; + height: 100%; + font-family: "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 14px; + } + + #asset-type-option-container { + padding: 7px; + flex: 1 1 auto; + overflow: auto; + max-width: 100%; + font-size: 16px; + } + + #asset-type-option-content { + padding: 7px 14px; + box-sizing: border-box; + height: 100%; + overflow: auto; + background: white; + border-radius: 7px; + } + + #type-list-filter { + padding: 7px; + } + + #filterInput { + width: 100%; + } + + #type-list-content { + padding: 0 7px; + overflow: hidden; + } + + #type-list { + padding: 0 7px; + background: white; + border-radius: 7px; + height: 100%; + overflow: auto; + text-transform: capitalize; + } + + #type-list-container { + display: flex; + flex-direction: column; + width: 320px; + gap: 4px; + } + + #type-title { + display: flex; + align-items: center; + margin: 9px 4px; + } + + #type-description { + text-transform: capitalize; + color: var(--or-app-color3, ${unsafeCSS(DefaultColor3)}); + margin-left: 10px; + font-size: 18px; + font-weight: bold; + font-family: "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + } + + .heading, + .mdc-list-group__subheader { + text-transform: uppercase; + font-family: "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-weight: bolder; + line-height: 1em; + color: var(--or-app-color3, ${unsafeCSS(DefaultColor3)}); + letter-spacing: 0.025em; + font-size: 14px; + margin: 20px 0 10px; + } + + .mdc-list-group__subheader { + margin: 20px 0 0 16px; + } + `; } constructor() { @@ -291,19 +308,23 @@ export class OrAddAssetDialog extends OrElement { -
- ${when(agentItems.length > 0, () => getListTemplate(filteredAgentItems, true))} -
- ${when(assetItems.length > 0, () => getListTemplate(filteredAssetItems, false))} +
+
+ ${when(agentItems.length > 0, () => getListTemplate(filteredAgentItems, true))} +
+ ${when(assetItems.length > 0, () => getListTemplate(filteredAssetItems, false))} +
- ${ - !this.selectedType - ? html`
` - : this.getTypeTemplate(this.selectedType, parentStr) - } +
+ ${ + !this.selectedType + ? html`
` + : this.getTypeTemplate(this.selectedType, parentStr) + } +
${ !this.showParentAssetSelector diff --git a/ui/component/or-asset-viewer/src/index.ts b/ui/component/or-asset-viewer/src/index.ts index 6c73e026d8..371ce19451 100644 --- a/ui/component/or-asset-viewer/src/index.ts +++ b/ui/component/or-asset-viewer/src/index.ts @@ -28,12 +28,10 @@ import "@openremote/or-attribute-history"; import "@openremote/or-chart"; import "@openremote/or-mwc-components/or-mwc-table"; import "@openremote/or-components/or-panel"; -import "@openremote/or-mwc-components/or-mwc-dialog"; -import { showOkCancelDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; -import "@openremote/or-mwc-components/or-mwc-list"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { type OrTranslate, translate } from "@openremote/or-translate"; -import { InputType, OrInputChangedEvent, OrMwcInput } from "@openremote/or-mwc-components/or-mwc-input"; -import manager, { OPENREMOTE_CLIENT_ID, RESTRICTED_USER_REALM_ROLE, subscribe, Util } from "@openremote/core"; +import { InputType } from "@openremote/or-mwc-components/or-mwc-input"; +import manager, { RESTRICTED_USER_REALM_ROLE, subscribe, Util } from "@openremote/core"; import type { OrMwcTable, OrMwcTableRowClickEvent } from "@openremote/or-mwc-components/or-mwc-table"; import type { OrChartConfig } from "@openremote/or-chart"; import type { HistoryConfig, OrAttributeHistory } from "@openremote/or-attribute-history"; @@ -67,10 +65,11 @@ import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar"; import { progressCircular } from "@openremote/or-mwc-components/style"; import { when } from "lit/directives/when.js"; import { ifDefined } from "lit/directives/if-defined.js"; -import type { OrVaadinCheckbox } from "@openremote/or-vaadin-components/or-vaadin-checkbox"; import type { OrVaadinInput } from "@openremote/or-vaadin-components/or-vaadin-input"; import { OrVaadinDateTimePicker } from "@openremote/or-vaadin-components/or-vaadin-date-time-picker"; import { getConfirmDialogContent, showConfirmDialog } from "@openremote/or-vaadin-components/or-vaadin-confirm-dialog"; +import type { OrVaadinCheckboxGroup } from "@openremote/or-vaadin-components/or-vaadin-checkbox-group"; +import { createRef, type Ref, ref } from "lit/directives/ref.js"; declare function require(name: string): any; @@ -841,36 +840,46 @@ function getPanelContent( }; const attributePickerModalOpen = () => { - const newlySelectedAttributes = [...selectedAttributes]; + let dialog: OrVaadinDialog | undefined; + const groupRef: Ref = createRef(); + const onCancel = () => { + dialog?.close(); + }; + const onOk = () => { + const groupElem = groupRef.value; + if (groupElem) { + updateSelectedAttributes(groupElem.value); + dialog?.close(); + } + }; - // TODO: Replace with or-vaadin-dialog, see https://github.com/openremote/core/issues/2594 - showOkCancelDialog( - i18next.t("addRemoveAttributes"), + dialog = showDialog( + hostElement.shadowRoot!, html` -
- ${availableAttributes.sort().map( - (attribute) => html` - selected === attribute)} - @change=${(ev: Event) => { - if ((ev.currentTarget as OrVaadinCheckbox).checked) { - newlySelectedAttributes.push(attribute); - } else { - newlySelectedAttributes.splice(newlySelectedAttributes.indexOf(attribute), 1); - } - }} - > - ` - )} -
+ +

+ +

+ + ${availableAttributes.sort().map( + (attr) => html` + + + + ` + )} + +
+ + + + + + +
+
` - ).then((ok) => { - if (ok) { - updateSelectedAttributes(newlySelectedAttributes); - } - }); + ); }; const headersAndRows = getHeadersAndRows(); diff --git a/ui/component/or-asset-viewer/src/or-add-attribute-panel.ts b/ui/component/or-asset-viewer/src/or-add-attribute-panel.ts index 052d4e8c47..919ede1842 100644 --- a/ui/component/or-asset-viewer/src/or-add-attribute-panel.ts +++ b/ui/component/or-asset-viewer/src/or-add-attribute-panel.ts @@ -70,7 +70,6 @@ export class OrAddAttributePanel extends OrElement { return css` #attribute-creator { min-width: 300px; - padding: 20px; display: flex; flex-direction: column; gap: 10px; @@ -129,11 +128,11 @@ export class OrAddAttributePanel extends OrElement { @change=${(ev: CustomEvent) => this.onValueTypeChanged((ev.currentTarget as OrVaadinComboBox).value)}> - this.onArrayChanged((ev.currentTarget as OrVaadinCheckbox).checked, 1)}> - this.onArrayChanged(true, Number((ev.currentTarget as OrVaadinNumberField).value))}> diff --git a/ui/component/or-asset-viewer/src/or-edit-asset-panel.ts b/ui/component/or-asset-viewer/src/or-edit-asset-panel.ts index afddc0df50..2a9ff38441 100644 --- a/ui/component/or-asset-viewer/src/or-edit-asset-panel.ts +++ b/ui/component/or-asset-viewer/src/or-edit-asset-panel.ts @@ -24,7 +24,7 @@ import { type ValueInputProviderOptions, getValueHolderInputTemplateProvider, } from "@openremote/or-vaadin-components/value-input-provider"; -import { InputType, type OrMwcInput, type OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input"; +import { InputType, type OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input"; import { i18next } from "@openremote/or-translate"; import { type Asset, @@ -36,8 +36,9 @@ import { import { DefaultColor5, DefaultColor3, Util } from "@openremote/core"; import "@openremote/or-mwc-components/or-mwc-input"; import type { OrIcon } from "@openremote/or-icon"; -import { showDialog, OrMwcDialog, type DialogAction } from "@openremote/or-mwc-components/or-mwc-dialog"; -import { type ListItem, ListType, type OrMwcList } from "@openremote/or-mwc-components/or-mwc-list"; +import { showDialog, type OrVaadinDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; +import type { OrVaadinCheckboxGroup } from "@openremote/or-vaadin-components/or-vaadin-checkbox-group"; +import type { OrVaadinButton } from "@openremote/or-vaadin-components/or-vaadin-button"; import "./or-add-attribute-panel"; import { getField, getPanel, getPropertyTemplate } from "./index"; import type { OrAddAttributePanelAttributeChangedEvent } from "./or-add-attribute-panel"; @@ -591,6 +592,7 @@ export class OrEditAssetPanel extends OrElement { protected _addAttribute() { const asset = this.asset!; let attr: Attribute; + const addBtnRef: Ref = createRef(); const isDisabled = (attribute: Attribute) => { return !( @@ -602,53 +604,42 @@ export class OrEditAssetPanel extends OrElement { ); }; - const onAttributeChanged = (attribute: Attribute) => { - const addDisabled = isDisabled(attribute); - const addBtn = dialog!.shadowRoot!.getElementById("add-btn") as OrMwcInput; - addBtn!.disabled = addDisabled; + const onAttributeChanged = (ev: OrAddAttributePanelAttributeChangedEvent) => { + const attribute = ev.detail; + addBtnRef.value!.disabled = isDisabled(attribute); attr = attribute; }; + const onOk = () => { + if (!isDisabled(attr)) { + this.asset.attributes![attr.name!] = attr; + this._onModified(); + this.requestUpdate(); + dialog?.close(); + } + }; + const dialog = showDialog( - new OrMwcDialog() - .setContent(html` + this.shadowRoot!, + html` + +

+ +

- `) - .setStyles(html` - - `) - .setHeading(i18next.t("addAttribute")) - .setActions([ - { - actionName: "cancel", - content: "cancel", - }, - { - default: true, - actionName: "add", - action: () => { - if (!isDisabled(attr)) { - this.asset.attributes![attr.name!] = attr; - this._onModified(); - this.requestUpdate(); - } - }, - content: html``, - }, - ]) - .setDismissAction(null) +
+ dialog?.close()}> + + + + + +
+
+ ` ); } @@ -660,85 +651,66 @@ export class OrEditAssetPanel extends OrElement { const meta = attribute.meta || {}; - const metaItemList: (ListItem | null)[] = assetTypeInfo.metaItemDescriptors - .map((metaName) => AssetModelUtil.getMetaItemDescriptor(metaName)!) - .filter((descriptor) => !meta.hasOwnProperty(descriptor.name!)) - .map((descriptor) => { - return { - text: Util.getMetaLabel(undefined, descriptor, this.asset!.type!, true), - value: descriptor.name!, - translate: false, - }; - }) - .sort(Util.sortByString((item) => item.text)); + const listRef: Ref = createRef(); + const onOk = () => { + const selectedItems = listRef.value?.value; + if (selectedItems) { + if (!attribute.meta) { + attribute.meta = {}; + } + selectedItems.forEach((item) => { + const descriptor = AssetModelUtil.getMetaItemDescriptors().find((descriptor) => descriptor.name === item); + if (descriptor) { + attribute.meta![descriptor.name!] = descriptor.type === "boolean" ? true : null; + this._onModified(); + this.requestUpdate(); + } + }); + } + dialog?.close(); + }; const dialog = showDialog( - new OrMwcDialog() - .setContent(html` -
- + this.shadowRoot!, + html` + +

+ +

+ + ${assetTypeInfo.metaItemDescriptors + .map((metaName) => AssetModelUtil.getMetaItemDescriptor(metaName)!) + .filter((descriptor) => !meta.hasOwnProperty(descriptor.name!)) + .map((descriptor) => ({ + descriptor, + label: Util.getMetaLabel(undefined, descriptor, this.asset!.type!, true), + })) + .sort(Util.sortByString((item) => item.label)) + .map( + (item) => html` + + ` + )} + +
+ dialog?.close()}> + + + + +
- `) - .setStyles(html` - - `) - .setHeading(i18next.t("addMetaItems")) - .setActions([ - { - actionName: "cancel", - content: "cancel", - }, - { - default: true, - actionName: "add", - action: () => { - const list = dialog!.shadowRoot!.getElementById("meta-creator-list") as OrMwcList; - const selectedItems = list ? list.selectedItems : undefined; - if (selectedItems) { - if (!attribute.meta) { - attribute.meta = {}; - } - selectedItems.forEach((item) => { - const descriptor = AssetModelUtil.getMetaItemDescriptors().find( - (descriptor) => descriptor.name === item.value - ); - if (descriptor) { - attribute.meta![descriptor.name!] = descriptor.type === "boolean" ? true : null; - this._onModified(); - this.requestUpdate(); - } - }); - } - }, - content: "add", - }, - ]) - .setDismissAction(null) +
+ ` ); } protected _getParentTemplate() { - let dialog: OrMwcDialog; + let dialog: OrVaadinDialog | undefined; + const assetTreeRef: Ref = createRef(); const setParent = () => { - const assetTree = dialog.shadowRoot!.getElementById("parent-asset-tree") as OrAssetTree; + const assetTree = assetTreeRef.value!; this.asset.parentId = assetTree.selectedIds!.length === 1 ? assetTree.selectedIds![0] : undefined; // Need to update the assets path as well const path = [this.asset.id!]; @@ -755,74 +727,51 @@ export class OrEditAssetPanel extends OrElement { this.asset.parentId = undefined; this.asset.path = [this.asset.id!]; this._onModified(); + dialog?.close(); }; - const blockEvent = (ev: Event) => { - ev.stopPropagation(); - }; + const blockEvent = (ev: Event) => ev.stopPropagation(); const parentSelector = (node: UiAssetTreeNode) => node.asset!.id !== this.asset.id; - // Prevent change event from bubbling up as it will affect any ancestor listeners that are interested in a different asset tree - const dialogContent = html``; - - const dialogActions: DialogAction[] = [ - { - actionName: "clear", - content: "none", - action: clearParent, - }, - { - actionName: "ok", - content: "ok", - action: setParent, - }, - { - default: true, - actionName: "cancel", - content: "cancel", - }, - ]; + const onOk = () => { + setParent(); + dialog?.close(); + }; const openDialog = () => { dialog = showDialog( - new OrMwcDialog() - .setContent(dialogContent) - .setActions(dialogActions) - .setStyles(html` - - `) - .setHeading(i18next.t("setParent")) - .setDismissAction(null) + this.shadowRoot!, + html` + +

+ +

+ +
+ dialog?.close()}> + + +
+ + + + + + +
+
+
+ ` ); }; diff --git a/ui/component/or-asset-viewer/test/fixtures/index.ts b/ui/component/or-asset-viewer/test/fixtures/index.ts index 28cffb3b79..32ceba5199 100644 --- a/ui/component/or-asset-viewer/test/fixtures/index.ts +++ b/ui/component/or-asset-viewer/test/fixtures/index.ts @@ -74,10 +74,11 @@ export class AssetViewer { await configItemLocator.getByRole("button", { name: "Add configuration items" }).click(); for (const item of items) { await this.page - .locator("li", { has: this.page.getByText(Util.camelCaseToSentenceCase(item), { exact: true }) }) + .getByRole("dialog") + .getByRole("checkbox", { name: Util.camelCaseToSentenceCase(item), exact: true }) .check(); } - await this.page.getByRole("button", { name: "Add", exact: true }).click(); + await this.page.getByRole("dialog").getByRole("button", { name: "Add", exact: true }).click(); } /** diff --git a/ui/component/or-attribute-barchart/src/index.ts b/ui/component/or-attribute-barchart/src/index.ts index 240fb35234..93dba35345 100644 --- a/ui/component/or-attribute-barchart/src/index.ts +++ b/ui/component/or-attribute-barchart/src/index.ts @@ -48,7 +48,7 @@ import { UniversalTransition } from "echarts/features"; import moment from "moment"; import { getAssetDescriptorIconTemplate } from "@openremote/or-icon"; import { isAxiosError } from "@openremote/rest"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar"; import { cache } from "lit/directives/cache.js"; import { createMenuBarItem, type MenuBarItem } from "@openremote/or-vaadin-components/or-vaadin-menu-bar"; @@ -648,7 +648,6 @@ export class OrAttributeBarChart extends OrElement { } protected _getTimeControlsTemplate(disabled: boolean): TemplateResult { - console.debug("Disabled?", disabled); const menuItems: MenuBarItem[] = [ { component: createMenuBarItem( @@ -855,47 +854,53 @@ export class OrAttributeBarChart extends OrElement { protected _openTimeDialog(startTimestamp?: number, endTimestamp?: number) { const startRef: Ref = createRef(); const endRef: Ref = createRef(); - showDialog( - new OrMwcDialog() - .setHeading(i18next.t("timeframe")) - .setContent( - () => html` -
- - - - - - -
- ` - ) - .setActions([ - { - actionName: "cancel", - content: "cancel", - }, - { - actionName: "ok", - content: "ok", - action: () => { - if (startRef.value?.value && endRef.value?.value && startRef.value.value < endRef.value.value) { - this._isCustomWindow = true; - this.timeframe = [new Date(startRef.value.value), new Date(endRef.value.value)]; - } else { - showSnackbar(undefined, i18next.t("errorOccurred")); - } - }, - }, - ]) + let dialog: OrVaadinDialog | undefined; + + const onCancel = () => { + dialog?.close(); + }; + const onOk = () => { + if (startRef.value?.value && endRef.value?.value && startRef.value.value < endRef.value.value) { + this._isCustomWindow = true; + this.timeframe = [new Date(startRef.value.value), new Date(endRef.value.value)]; + dialog?.close(); + } else { + showSnackbar(undefined, i18next.t("errorOccurred")); + } + }; + dialog = showDialog( + this.shadowRoot!, + html` + +

+ +

+
+ + + + + + +
+
+ + + + + + +
+
+ ` ); } diff --git a/ui/component/or-attribute-input/src/index.ts b/ui/component/or-attribute-input/src/index.ts index e52012a060..089b6234e0 100644 --- a/ui/component/or-attribute-input/src/index.ts +++ b/ui/component/or-attribute-input/src/index.ts @@ -348,10 +348,11 @@ export class OrAttributeInput extends subscribe(manager)(translate(i18next)(OrEl #wrapper or-map { min-height: 250px; + aspect-ratio: 3/1 !important; } #wrapper .long-press-msg { - display: none; + display: none !important; } #wrapper { diff --git a/ui/component/or-chart/src/index.ts b/ui/component/or-chart/src/index.ts index 376c6abe7f..ac20c8c030 100644 --- a/ui/component/or-chart/src/index.ts +++ b/ui/component/or-chart/src/index.ts @@ -56,7 +56,8 @@ import { OrAssetTreeSelectionEvent } from "@openremote/or-asset-tree"; import { getAssetDescriptorIconTemplate } from "@openremote/or-icon"; import { type GenericAxiosResponse, isAxiosError } from "@openremote/rest"; import { OrAssetAttributePicker, OrAssetAttributePickerPickedEvent } from "@openremote/or-attribute-picker"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog as showVaadinDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { cache } from "lit/directives/cache.js"; import debounce from "lodash.debounce"; import { OrVaadinDateTimePicker } from "@openremote/or-vaadin-components/or-vaadin-date-time-picker"; @@ -64,6 +65,7 @@ import { when } from "lit/directives/when.js"; import { map } from "lit/directives/map.js"; import { createRef, type Ref, ref } from "lit/directives/ref.js"; import { createMenuBarItem, type MenuBarItem } from "@openremote/or-vaadin-components/or-vaadin-menu-bar"; +import { showSnackbar } from "@openremote/or-mwc-components/or-mwc-snackbar"; echarts.use([ GridComponent, @@ -161,27 +163,27 @@ const tableStyle = require("@material/data-table/dist/mdc.data-table.css"); // language=CSS const style = css` :host { - + --internal-or-chart-background-color: var(--or-chart-background-color, var(--or-app-color2, ${unsafeCSS(DefaultColor2)})); --internal-or-chart-border-color: var(--or-chart-border-color, rgba(76, 76, 76, 0.6)); --internal-or-chart-text-color: var(--or-chart-text-color, var(--or-app-color3, ${unsafeCSS(DefaultColor3)})); - --internal-or-chart-controls-margin: var(--or-chart-controls-margin, 0 0 20px 0); - --internal-or-chart-controls-margin-children: var(--or-chart-controls-margin-children, 0 auto 20px auto); - --internal-or-chart-graph-fill-color: var(--or-chart-graph-fill-color, var(--or-app-color4, ${unsafeCSS(DefaultColor4)})); - --internal-or-chart-graph-fill-opacity: var(--or-chart-graph-fill-opacity, 0.25); - --internal-or-chart-graph-line-color: var(--or-chart-graph-line-color, var(--or-app-color4, ${unsafeCSS(DefaultColor4)})); + --internal-or-chart-controls-margin: var(--or-chart-controls-margin, 0 0 20px 0); + --internal-or-chart-controls-margin-children: var(--or-chart-controls-margin-children, 0 auto 20px auto); + --internal-or-chart-graph-fill-color: var(--or-chart-graph-fill-color, var(--or-app-color4, ${unsafeCSS(DefaultColor4)})); + --internal-or-chart-graph-fill-opacity: var(--or-chart-graph-fill-opacity, 0.25); + --internal-or-chart-graph-line-color: var(--or-chart-graph-line-color, var(--or-app-color4, ${unsafeCSS(DefaultColor4)})); --internal-or-chart-graph-point-color: var(--or-chart-graph-point-color, var(--or-app-color3, ${unsafeCSS(DefaultColor3)})); --internal-or-chart-graph-point-border-color: var(--or-chart-graph-point-border-color, var(--or-app-color5, ${unsafeCSS(DefaultColor5)})); --internal-or-chart-graph-point-radius: var(--or-chart-graph-point-radius, 4); - --internal-or-chart-graph-point-hit-radius: var(--or-chart-graph-point-hit-radius, 20); + --internal-or-chart-graph-point-hit-radius: var(--or-chart-graph-point-hit-radius, 20); --internal-or-chart-graph-point-border-width: var(--or-chart-graph-point-border-width, 2); - --internal-or-chart-graph-point-hover-color: var(--or-chart-graph-point-hover-color, var(--or-app-color5, ${unsafeCSS(DefaultColor5)})); + --internal-or-chart-graph-point-hover-color: var(--or-chart-graph-point-hover-color, var(--or-app-color5, ${unsafeCSS(DefaultColor5)})); --internal-or-chart-graph-point-hover-border-color: var(--or-chart-graph-point-hover-border-color, var(--or-app-color3, ${unsafeCSS(DefaultColor3)})); - --internal-or-chart-graph-point-hover-radius: var(--or-chart-graph-point-hover-radius, 4); + --internal-or-chart-graph-point-hover-radius: var(--or-chart-graph-point-hover-radius, 4); --internal-or-chart-graph-point-hover-border-width: var(--or-chart-graph-point-hover-border-width, 2); - + width: 100%; - display: block; + display: block; } .line-label { @@ -201,7 +203,7 @@ const style = css` background-size: 10px 16px; background-repeat: repeat-y; } - + .button-icon { align-self: center; padding: 11px 6px; @@ -221,11 +223,11 @@ const style = css` min-width: 600px; height: calc(100vh - 50%); } - + :host([hidden]) { display: none; } - + #container { display: flex; min-width: 0; @@ -233,7 +235,7 @@ const style = css` height: 100%; gap: 8px; } - + #msg { height: 100%; width: 100%; @@ -241,11 +243,11 @@ const style = css` align-items: center; text-align: center; } - + #msg:not([hidden]) { - display: flex; + display: flex; } - + #period-controls { display: flex; flex-direction: column; @@ -264,7 +266,7 @@ const style = css` #period-dropdown-controls > *:last-child { margin-left: -2px; } - + #controls { display: flex; @@ -284,7 +286,7 @@ const style = css` .attribute-list-dense { flex-wrap: wrap; } - + .attribute-list-item { cursor: pointer; display: flex; @@ -315,7 +317,7 @@ const style = css` .button-clear:hover { --or-icon-fill: var(--or-app-color4); } - + .attribute-list-item-label { display: flex; flex: 1 1 0; @@ -1233,45 +1235,53 @@ export class OrChart extends translate(i18next)(OrElement) { protected _openTimeDialog(startTimestamp?: number, endTimestamp?: number) { const startRef: Ref = createRef(); const endRef: Ref = createRef(); - showDialog( - new OrMwcDialog() - .setHeading(i18next.t("timeframe")) - .setContent( - () => html` -
- - - - - - -
- ` - ) - .setActions([ - { - actionName: "cancel", - content: "cancel", - }, - { - actionName: "ok", - content: "ok", - action: () => { - if (startRef.value?.value && endRef.value?.value) { - this._isCustomWindow = true; - this.timeframe = [new Date(startRef.value.value), new Date(endRef.value.value)]; - } - }, - }, - ]) + let dialog: OrVaadinDialog | undefined; + + const onCancel = () => { + dialog?.close(); + }; + const onOk = () => { + if (startRef.value?.value && endRef.value?.value && startRef.value.value < endRef.value.value) { + this._isCustomWindow = true; + this.timeframe = [new Date(startRef.value.value), new Date(endRef.value.value)]; + dialog?.close(); + } else { + showSnackbar(undefined, i18next.t("errorOccurred")); + } + }; + dialog = showVaadinDialog( + this.shadowRoot!, + html` + +

+ +

+
+ + + + + + +
+
+ + + + + + +
+
+ ` ); } diff --git a/ui/component/or-dashboard-builder/src/panels/assettypes-panel.ts b/ui/component/or-dashboard-builder/src/panels/assettypes-panel.ts index 126a312284..7dbd371f11 100644 --- a/ui/component/or-dashboard-builder/src/panels/assettypes-panel.ts +++ b/ui/component/or-dashboard-builder/src/panels/assettypes-panel.ts @@ -26,7 +26,7 @@ import { Util } from "@openremote/core"; import { when } from "lit/directives/when.js"; import { createRef, type Ref, ref } from "lit/directives/ref.js"; import type { AssetTreeConfig, OrAssetTree } from "@openremote/or-asset-tree"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { type ComboBoxLitRenderer, comboBoxRenderer, @@ -314,56 +314,49 @@ export class AssettypesPanel extends OrElement { multiSelect: multi, }, } as AssetTreeConfig; - const dialog = showDialog( - new OrMwcDialog() - .setHeading(i18next.t("linkedAssets")) - .setStyles( - html`` - ) - .setContent(html` -
- + + let dialog: OrVaadinDialog | undefined; + const onCancel = () => dialog?.close(); + const onOk = () => { + const tree = assetTreeRef.value; + if (tree?.selectedIds) { + if (multi) { + this.assetIds = tree.selectedIds; + } else { + this.assetIds = tree.selectedIds[0]; + } + } + dialog?.close(); + }; + + dialog = showDialog( + this.shadowRoot!, + html` + +

+ +

+ +
+ + + + + +
- `) - .setActions([ - { - default: true, - actionName: "cancel", - content: "cancel", - }, - { - actionName: "ok", - content: "ok", - action: () => { - const tree = assetTreeRef.value; - if (tree?.selectedIds) { - if (multi) { - this.assetIds = tree.selectedIds; - } else { - this.assetIds = tree.selectedIds[0]; - } - } - }, - }, - ]) - .setDismissAction({ - actionName: "cancel", - }) +
+ ` ); } } diff --git a/ui/component/or-dashboard-builder/src/settings/barchart-settings.ts b/ui/component/or-dashboard-builder/src/settings/barchart-settings.ts index b63e15a5d8..7370c18230 100644 --- a/ui/component/or-dashboard-builder/src/settings/barchart-settings.ts +++ b/ui/component/or-dashboard-builder/src/settings/barchart-settings.ts @@ -33,9 +33,8 @@ import type { BarChartWidgetConfig } from "../widgets/barchart-widget"; import type { OrVaadinToggle } from "@openremote/or-vaadin-components/or-vaadin-toggle"; import { when } from "lit/directives/when.js"; import type moment from "moment/moment"; -import { type ListItem, ListType, type OrMwcList } from "@openremote/or-mwc-components/or-mwc-list"; import type { OrVaadinSelect, SelectItem } from "@openremote/or-vaadin-components/or-vaadin-select"; -import { showDialog, OrMwcDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { type BarChartAttributeConfig, type BarChartInterval, @@ -48,6 +47,7 @@ import { createRef, type Ref, ref } from "lit/directives/ref.js"; import type { AttributesChartPanel } from "../panels/attributes-chart-panel"; import debounce from "lodash.debounce"; import type { OrVaadinNumberField } from "@openremote/or-vaadin-components/or-vaadin-number-field"; +import type { OrVaadinCheckboxGroup } from "@openremote/or-vaadin-components/or-vaadin-checkbox-group"; const styling = css` .switch-container { @@ -640,66 +640,60 @@ export class BarChartSettings extends WidgetSettings { "methodModeAttributes", "methodSumAttributes", ]; - const methodList: ListItem[] = methodKeys.map((key) => { - const attributeRefs = this.widgetConfig.attributeSettings[key]; - const isActive = attributeRefs?.some( - (attrRef) => attrRef.id === attributeRef.id && attrRef.name === attributeRef.name - ); - return { - text: key, - value: key, - data: isActive ? key : undefined, - translate: true, - }; - }); + const activeKeys = methodKeys.filter((key) => + this.widgetConfig?.attributeSettings?.[key]?.find( + (ref) => ref.id === attributeRef.id && ref.name === attributeRef.name + ) + ); + + const groupRef: Ref = createRef(); + let dialog: OrVaadinDialog | undefined; + + const onCancel = () => { + dialog?.close(); + }; + const onOk = () => { + const groupElem = groupRef.value; + if (groupElem) { + // Find all "active" methods for this AttributeRef, and remove them all. (so they become empty arrays) + activeKeys.forEach((key) => { + this.toggleAttributeSetting(key, attributeRef, false); + }); + // Add the methods that were checked in the checkbox list + groupElem.value.forEach((key) => { + this.toggleAttributeSetting(key as keyof BarChartAttributeConfig, attributeRef, false); + }); + this.notifyConfigUpdate(); + dialog?.close(); + } + }; - const listRef: Ref = createRef(); - - showDialog( - new OrMwcDialog() - .setContent(html` -
- + dialog = showDialog( + this.shadowRoot!, + html` + +

+ +

+ + ${methodKeys.map( + (method) => html` + + + + ` + )} + +
+ + + + + +
- `) - .setHeading(i18next.t("algorithmMethod")) - .setActions([ - { - actionName: "cancel", - content: "cancel", - }, - { - default: true, - actionName: "ok", - action: () => { - if (listRef.value) { - // Find all "active" methods for this AttributeRef, and remove them all. (so they become empty arrays) - methodKeys - .filter( - (oldKey) => - !!this.widgetConfig.attributeSettings[oldKey]?.find( - (attrRef) => attrRef.id === attributeRef.id && attrRef.name === attributeRef.name - ) - ) - .forEach((oldKey) => this.toggleAttributeSetting(oldKey, attributeRef, false)); - - // Add the methods that were checked in the checkbox list - (listRef.value.values as (keyof BarChartAttributeConfig)[] | undefined)?.forEach((s) => { - this.toggleAttributeSetting(s, attributeRef, false); - }); - this.notifyConfigUpdate(); - } - }, - content: "ok", - }, - ]) - .setDismissAction(null) +
+ ` ); } diff --git a/ui/component/or-map/package.json b/ui/component/or-map/package.json index f23c06f3dc..ab932075d0 100644 --- a/ui/component/or-map/package.json +++ b/ui/component/or-map/package.json @@ -33,7 +33,6 @@ "@openremote/model": "workspace:*", "@openremote/or-element": "workspace:*", "@openremote/or-icon": "workspace:*", - "@openremote/or-mwc-components": "workspace:*", "@openremote/or-translate": "workspace:*", "@openremote/or-vaadin-components": "workspace:*", "lit": "^3.3.1", diff --git a/ui/component/or-map/src/index.ts b/ui/component/or-map/src/index.ts index e359534505..de5ecdc55f 100644 --- a/ui/component/or-map/src/index.ts +++ b/ui/component/or-map/src/index.ts @@ -31,9 +31,8 @@ import type { ValueInputProviderGenerator, ValueInputTemplateFunction, } from "@openremote/or-vaadin-components/value-input-provider"; -import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog"; +import { type OrVaadinDialog, showDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { getMarkerIconAndColorFromAssetType } from "./util"; -import { i18next } from "@openremote/or-translate"; import debounce from "lodash.debounce"; import type { AttributeEvent, GeoJsonConfig } from "@openremote/model"; import { @@ -46,6 +45,7 @@ import { AssetMap } from "./asset-map"; import { OrMapCenterControl } from "./controls/center"; import { OrMapGeolocateControl } from "./controls/geolocate"; import { ifDefined } from "lit-html/directives/if-defined.js"; +import { createRef, ref, type Ref } from "lit/directives/ref.js"; import type { AssetWithLocation, ClusterConfig, ControlPosition, MapEventDetail, MapFilter } from "./types"; // Re-exports @@ -179,18 +179,19 @@ export const geoJsonPointInputTemplateProvider: ValueInputProviderGenerator = ( const iconAndColor = getMarkerIconAndColorFromAssetType(assetDescriptor); - let dialog: OrMwcDialog | undefined; + let dialog: OrVaadinDialog | undefined; + const orMap: Ref = createRef(); - _setPos = (lngLat: LngLatLike | null) => { + const setPos = (lngLat: LngLatLike | null) => { if (readonly || disabled) { return; } pos = lngLat ? Util.getLngLat(lngLat) : null; - if (dialog) { + if (orMap.value) { // We're in compact mode modal - const marker = dialog.shadowRoot!.getElementById("geo-json-point-marker") as OrMapMarker; + const marker = orMap.value.firstElementChild as OrMapMarker; marker.lng = pos ? pos.lng : undefined; marker.lat = pos ? pos.lat : undefined; center = pos ? Object.values(pos) : undefined; @@ -200,12 +201,12 @@ export const geoJsonPointInputTemplateProvider: ValueInputProviderGenerator = ( valueChangeHandler(pos as LngLatLike); } }; - const setPos = _setPos; let content = html` - ${i18next.t("longPressSetLoc")} + + + `; if (compact) { @@ -231,45 +234,39 @@ export const geoJsonPointInputTemplateProvider: ValueInputProviderGenerator = ( const onClick = () => { dialog = showDialog( - new OrMwcDialog() - .setContent(mapContent) - .setStyles(html` - - `) - .setActions([ - { - actionName: "none", - content: "none", - action: () => { - setPos(null); - valueChangeHandler(pos as LngLatLike); - }, - }, - { - actionName: "ok", - content: "ok", - action: () => { - valueChangeHandler(pos as LngLatLike); - }, - }, - { - default: true, - actionName: "cancel", - content: "cancel", - }, - ]) + document.body, + html` + +
+ ${mapContent} +
+ dialog?.close()}> + + +
+ { + setPos(null); + valueChangeHandler(pos as LngLatLike); + dialog?.close(); + }} + > + + + { + valueChangeHandler(pos as LngLatLike); + dialog?.close(); + }} + > + + +
+
+
+ ` ); }; diff --git a/ui/component/or-map/tsconfig.json b/ui/component/or-map/tsconfig.json index b4a1315023..1b39379857 100644 --- a/ui/component/or-map/tsconfig.json +++ b/ui/component/or-map/tsconfig.json @@ -12,7 +12,6 @@ { "path": "../rest" }, { "path": "../model" }, { "path": "../or-translate" }, - { "path": "../or-mwc-components" }, { "path": "../or-icon" }, { "path": "../or-vaadin-components" } ] diff --git a/ui/component/or-rules/src/json-viewer/modals/or-rule-radial-modal.ts b/ui/component/or-rules/src/json-viewer/modals/or-rule-radial-modal.ts index 2ee9629cd8..c44db57040 100644 --- a/ui/component/or-rules/src/json-viewer/modals/or-rule-radial-modal.ts +++ b/ui/component/or-rules/src/json-viewer/modals/or-rule-radial-modal.ts @@ -18,21 +18,15 @@ */ import { html } from "lit"; import { OrElement } from "@openremote/or-element"; -import { customElement, property } from "lit/decorators.js"; +import { customElement, property, query } from "lit/decorators.js"; import type { AssetDescriptor, AttributePredicate, AssetQuery, RadialGeofencePredicate } from "@openremote/model"; -import { getAssetTypeFromQuery } from "../../index"; -import "@openremote/or-mwc-components/or-mwc-input"; -import { InputType } from "@openremote/or-mwc-components/or-mwc-input"; +import type { OrVaadinDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; import { i18next, translate } from "@openremote/or-translate"; import { OrRulesJsonRuleChangedEvent } from "../or-rule-json-viewer"; - -import { - type DialogAction, - type OrMwcDialog, - OrMwcDialogOpenedEvent, -} from "@openremote/or-mwc-components/or-mwc-dialog"; import { type OrMap, OrMapClickedEvent, type LngLatLike } from "@openremote/or-map"; import "@openremote/or-map"; +import type { OrVaadinNumberField } from "@openremote/or-vaadin-components/or-vaadin-number-field"; +import type { OrVaadinButton } from "@openremote/or-vaadin-components/or-vaadin-button"; @customElement("or-rule-radial-modal") export class OrRuleRadialModal extends translate(i18next)(OrElement) { @@ -48,21 +42,24 @@ export class OrRuleRadialModal extends translate(i18next)(OrElement) { @property({ type: Object }) public query?: AssetQuery; - constructor() { - super(); - this.addEventListener(OrMwcDialogOpenedEvent.NAME, this.initRadialMap); - } + @query("#radial-modal") + protected _mapDialogElem?: OrVaadinDialog; + + @query("#radial-modal-button") + protected _mapDialogButton?: OrVaadinButton; initRadialMap() { - const modal = this.shadowRoot!.getElementById("radial-modal"); - if (!modal) return; + if (!this._mapDialogElem) { + console.warn("Could not find radial map dialog element!"); + return; + } - const map = modal.shadowRoot!.querySelector(".or-map") as OrMap; + const map = this._mapDialogElem!.querySelector(".or-map") as OrMap; if (map) { map.addEventListener(OrMapClickedEvent.NAME, (evt: CustomEvent) => { const lngLat: any = evt.detail.lngLat; - const latElement = modal.shadowRoot!.querySelector(".location-lat") as HTMLInputElement; - const lngElement = modal.shadowRoot!.querySelector(".location-lng") as HTMLInputElement; + const latElement = this._mapDialogElem!.querySelector(".location-lat") as HTMLInputElement; + const lngElement = this._mapDialogElem!.querySelector(".location-lng") as HTMLInputElement; latElement.value = lngLat.lat; lngElement.value = lngLat.lng; @@ -73,8 +70,8 @@ export class OrRuleRadialModal extends translate(i18next)(OrElement) { this.setValuePredicateProperty("lng", lngLat.lng); }); - const latElement = modal.shadowRoot!.querySelector(".location-lat") as HTMLInputElement; - const lngElement = modal.shadowRoot!.querySelector(".location-lng") as HTMLInputElement; + const latElement = this._mapDialogElem!.querySelector(".location-lat") as HTMLInputElement; + const lngElement = this._mapDialogElem!.querySelector(".location-lng") as HTMLInputElement; if (lngElement.value && latElement.value) { const LngLat: LngLatLike = [parseFloat(lngElement.value), parseFloat(latElement.value)]; map.flyTo(LngLat, 15); @@ -84,10 +81,6 @@ export class OrRuleRadialModal extends translate(i18next)(OrElement) { } } - protected getAttributeName(attributePredicate: AttributePredicate): string | undefined { - return attributePredicate && attributePredicate.name ? attributePredicate.name.value : undefined; - } - protected setValuePredicateProperty(propertyName: string, value: any) { if (!this.attributePredicate) return; if (!this.attributePredicate.value) return; @@ -100,57 +93,54 @@ export class OrRuleRadialModal extends translate(i18next)(OrElement) { this.requestUpdate(); } - renderDialogHTML(value: RadialGeofencePredicate) { - const dialog: OrMwcDialog = this.shadowRoot!.getElementById("radial-modal") as OrMwcDialog; - - if (dialog) { - dialog.content = html`
- - - - -
- - -
- - + getDialogHTML(value: RadialGeofencePredicate) { + return html`
+ + + + +
-
`; - } + +
+ + { + const elem = ev.currentTarget as OrVaadinNumberField; + if (elem.checkValidity()) { + this.setValuePredicateProperty("radius", parseInt(elem.value)); + } + this._mapDialogButton!.disabled = !elem.checkValidity(); + }} + > + + +
`; } protected render() { @@ -161,44 +151,29 @@ export class OrRuleRadialModal extends translate(i18next)(OrElement) { if (!this.assetDescriptor || !valuePredicate) { return html``; } - - const attributeName = this.getAttributeName(this.attributePredicate); - const assetType = getAssetTypeFromQuery(this.query); // @ts-ignore const value: RadialGeofencePredicate = valuePredicate || undefined; - const radiusPickerModalActions: DialogAction[] = [ - { - actionName: "cancel", - content: html``, - action: () => { - // Nothing to do here - }, - }, - { - actionName: "ok", - default: true, - content: html``, - action: () => {}, - }, - ]; - const radialPickerModalOpen = () => { - const dialog: OrMwcDialog = this.shadowRoot!.getElementById("radial-modal") as OrMwcDialog; - if (dialog) { - dialog.dismissAction = null; - dialog.open(); - this.renderDialogHTML(value); - } + this._mapDialogElem?.open(); + this.initRadialMap(); }; - this.renderDialogHTML(value); - return html` radialPickerModalOpen()}> - + +

+ +

+ ${this.getDialogHTML(value)} +
+ this._mapDialogElem?.close()}> + + +
+
`; } } diff --git a/ui/component/or-rules/src/json-viewer/or-rule-trigger-query.ts b/ui/component/or-rules/src/json-viewer/or-rule-trigger-query.ts index b157904698..e3f091859c 100644 --- a/ui/component/or-rules/src/json-viewer/or-rule-trigger-query.ts +++ b/ui/component/or-rules/src/json-viewer/or-rule-trigger-query.ts @@ -17,26 +17,21 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import { type GeoJSONPoint, type RuleCondition, SunPositionTriggerPosition } from "@openremote/model"; -import { InputType, OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input"; -import { css, html, type PropertyValues } from "lit"; +import { css, html, type TemplateResult, type PropertyValues } from "lit"; import { OrElement } from "@openremote/or-element"; -import { customElement, property, state } from "lit/decorators.js"; +import { customElement, property, query, state } from "lit/decorators.js"; import moment from "moment"; import { buttonStyle } from "../style"; import { OrRulesJsonRuleChangedEvent } from "./or-rule-json-viewer"; import { TimeTriggerType } from "../index"; import { Util } from "@openremote/core"; -import { - type DialogAction, - type OrMwcDialog, - OrMwcDialogOpenedEvent, -} from "@openremote/or-mwc-components/or-mwc-dialog"; import { type OrMap, OrMapClickedEvent } from "@openremote/or-map"; import { i18next } from "@openremote/or-translate"; import type { OrVaadinSelect } from "@openremote/or-vaadin-components/or-vaadin-select"; import { when } from "lit/directives/when.js"; import { ifDefined } from "lit/directives/if-defined.js"; import type { OrVaadinTimePicker } from "@openremote/or-vaadin-components/or-vaadin-time-picker"; +import type { OrVaadinDialog } from "@openremote/or-vaadin-components/or-vaadin-dialog"; // language=CSS const style = css` @@ -90,6 +85,9 @@ export class OrRuleTriggerQuery extends OrElement { @state() protected triggerOptions: TimeTrigger[]; + @query("#map-modal") + protected _mapDialogElem?: OrVaadinDialog; + constructor() { super(); this.triggerOptions = []; @@ -100,8 +98,6 @@ export class OrRuleTriggerQuery extends OrElement { this.triggerOptions.push({ key: opt, value: this.triggerToString(opt) }); }); this.selectedTrigger = this.triggerOptions[0]; - - this.addEventListener(OrMwcDialogOpenedEvent.NAME, this.initMap); } updated(changedProperties: PropertyValues) { @@ -123,91 +119,73 @@ export class OrRuleTriggerQuery extends OrElement { /* ---------------------- */ initMap() { - const modal = this.shadowRoot!.getElementById("map-modal"); - if (!modal) return; - - const map = modal.shadowRoot!.querySelector(".or-map") as OrMap; - if (map) { - map.addEventListener(OrMapClickedEvent.NAME, (evt: CustomEvent) => { - const lngLat: any = evt.detail.lngLat; - this.setLocation({ type: "Point", coordinates: [lngLat.lat, lngLat.lng] }); - const latElement = modal.shadowRoot!.querySelector(".location-lat") as HTMLInputElement; - const lngElement = modal.shadowRoot!.querySelector(".location-lng") as HTMLInputElement; - latElement.value = lngLat.lat; - lngElement.value = lngLat.lng; - }); + if (!this._mapDialogElem) { + console.warn("Could not find map dialog element!"); + return; + } + const map = this._mapDialogElem.querySelector(".or-map") as OrMap; + if (!map) { + console.warn("Could not find map within dialog element!"); + return; } + map.addEventListener(OrMapClickedEvent.NAME, (evt: CustomEvent) => { + const lngLat: any = evt.detail.lngLat; + this.setLocation({ type: "Point", coordinates: [lngLat.lat, lngLat.lng] }); + const latElement = this._mapDialogElem!.querySelector(".location-lat") as HTMLInputElement; + const lngElement = this._mapDialogElem!.querySelector(".location-lng") as HTMLInputElement; + latElement.value = lngLat.lat; + lngElement.value = lngLat.lng; + }); } - renderDialogHTML(point: GeoJSONPoint | undefined) { - const dialog: OrMwcDialog = this.shadowRoot!.getElementById("map-modal") as OrMwcDialog; - if (dialog) { - dialog.content = html` -
- - ${ - point && point.coordinates - ? html` - - ` - : undefined - } - -
- - -
+ renderDialogHTML(point: GeoJSONPoint | undefined): TemplateResult { + return html` +
+ + ${ + point && point.coordinates + ? html` + + ` + : undefined + } + +
+ +
- `; - } +
+ `; } /* ----------------------------- */ render() { - const modalActions: DialogAction[] = [ - { - actionName: "close", - default: true, - content: html``, - action: () => {}, - }, - ]; - const onMapModalOpen = () => { - const dialog: OrMwcDialog = this.shadowRoot!.getElementById("map-modal") as OrMwcDialog; - if (dialog) { - dialog.dismissAction = null; - dialog.open(); - this.renderDialogHTML(this.condition.sun?.location); - } + const openModal = () => { + this._mapDialogElem?.open(); + this.initMap(); }; - // Render dialog on every update (for example when changing location in the modal itself) - this.renderDialogHTML(this.condition.sun?.location); return html`
@@ -249,14 +227,20 @@ export class OrRuleTriggerQuery extends OrElement { > - onMapModalOpen()}> + openModal()}> - + +

+ +

+ ${this.renderDialogHTML(this.condition.sun?.location)} +
+ this._mapDialogElem?.close()}> + + +
+
` } ` diff --git a/ui/component/or-vaadin-components/src/or-vaadin-confirm-dialog.ts b/ui/component/or-vaadin-components/src/or-vaadin-confirm-dialog.ts index 43b7aaa410..bbe904355c 100644 --- a/ui/component/or-vaadin-components/src/or-vaadin-confirm-dialog.ts +++ b/ui/component/or-vaadin-components/src/or-vaadin-confirm-dialog.ts @@ -55,6 +55,7 @@ export function showConfirmDialog(host: Node, dialog: TemplateResult) { return; } host.appendChild(container); + return dialogElem; } /** @@ -65,17 +66,17 @@ export function showConfirmDialog(host: Node, dialog: TemplateResult) { * @param theme - Optional theme to use for the dialog and its buttons (for example 'error'). * @param header - Dialog header that is either a translation key, or a {@link TemplateResult} * @param content - Dialog content that is either a translation key, or a {@link TemplateResult} - * @param confirmKey - Translation key to display inside the "confirm" button. If `undefined` the button will not be visible. - * @param cancelKey - Translation key to display inside the "cancel" button. If `undefined` the button will not be visible. - * @param rejectKey - Translation key to display inside the "reject" button. If `undefined` the button will not be visible. + * @param confirm - {@link TemplateResult} or translation key to display inside the "confirm" button. If `undefined` the button will not be visible. + * @param cancel - {@link TemplateResult} or translation key to display inside the "cancel" button. If `undefined` the button will not be visible. + * @param reject - {@link TemplateResult} or translation key to display inside the "reject" button. If `undefined` the button will not be visible. */ export function getConfirmDialogContent( theme: string | undefined, header: TemplateResult | string, content: TemplateResult | string, - confirmKey?: string, - cancelKey?: string, - rejectKey?: string + confirm?: TemplateResult | string, + cancel?: TemplateResult | string, + reject?: TemplateResult | string ): TemplateResult { return html` ${ @@ -85,28 +86,31 @@ export function getConfirmDialogContent( } ${typeof content === "string" ? html`` : content} ${when( - confirmKey, + typeof confirm === "string", () => html` - + - ` + `, + confirm ? () => confirm : undefined )} ${when( - cancelKey, + typeof cancel === "string", () => html` - + - ` + `, + cancel ? () => cancel : undefined )} ${when( - rejectKey, + typeof reject === "string", () => html` - + - ` + `, + reject ? () => reject : undefined )} `; } diff --git a/yarn.lock b/yarn.lock index e547d00c13..ae7707af77 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2233,7 +2233,6 @@ __metadata: "@openremote/model": "workspace:*" "@openremote/or-element": "workspace:*" "@openremote/or-icon": "workspace:*" - "@openremote/or-mwc-components": "workspace:*" "@openremote/or-translate": "workspace:*" "@openremote/or-vaadin-components": "workspace:*" "@openremote/util": "workspace:*"