diff --git a/ui/app/manager/index.html b/ui/app/manager/index.html index 6108ad377d..26d4dfb3e6 100644 --- a/ui/app/manager/index.html +++ b/ui/app/manager/index.html @@ -55,8 +55,6 @@ } body { margin: 0; - font-family: "Segoe UI", Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; - line-height: 1.5; height: 100%; -webkit-font-smoothing: antialiased; overflow: hidden; diff --git a/ui/app/manager/package.json b/ui/app/manager/package.json index 8dc53d3f12..d5f7bf7de5 100644 --- a/ui/app/manager/package.json +++ b/ui/app/manager/package.json @@ -22,8 +22,11 @@ "modelWatch": "npx orutil watch", "doctor": "npx cross-env NODE_OPTIONS=--max_old_space_size=4096 RSDOCTOR=true rspack --mode production", "build": "npx cross-env NODE_OPTIONS=--max_old_space_size=4096 rspack --mode production", + "build:experimental": "npx cross-env NODE_OPTIONS=--max_old_space_size=4096 rsbuild build", "serve": "npx tsc -b --clean && npx shx rm -rf dist lib && npx orutil build && npx cross-env NODE_OPTIONS=--max_old_space_size=4096 rspack serve --mode development", + "serve:experimental": "npx tsc -b --clean && npx shx rm -rf dist lib && npx orutil build && npx cross-env NODE_OPTIONS=--max_old_space_size=4096 rsbuild dev", "serveNoModelBuild": "npx tsc -b --clean && npx cross-env NODE_OPTIONS=--max_old_space_size=4096 rspack serve --mode development", + "serveNoModelBuild:experimental": "npx tsc -b --clean && npx cross-env NODE_OPTIONS=--max_old_space_size=4096 rsbuild dev", "test": "npx playwright test", "prepack": "npx tsc -b" }, @@ -46,9 +49,10 @@ "@openremote/or-rules": "workspace:*", "@openremote/or-services": "workspace:*", "@openremote/or-translate": "workspace:*", + "@openremote/or-vaadin-components": "workspace:*", "@openremote/rest": "workspace:*", + "@openremote/theme": "workspace:*", "@reduxjs/toolkit": "^1.8.1", - "iso-639-1": "^3.1.3", "lit": "^3.3.1", "maplibre-gl": "^5.12.0", "moment": "^2.29.4", @@ -56,6 +60,7 @@ }, "devDependencies": { "@openremote/util": "workspace:*", + "@rsbuild/core": "~1.7.5", "@rsdoctor/rspack-plugin": "~1.4.0", "@rspack/core": "~1.7.10" }, diff --git a/ui/app/manager/rsbuild.config.ts b/ui/app/manager/rsbuild.config.ts new file mode 100644 index 0000000000..0a01b8e99b --- /dev/null +++ b/ui/app/manager/rsbuild.config.ts @@ -0,0 +1,60 @@ +import { defineConfig } from "@rsbuild/core"; + +export default defineConfig({ + dev: { + hmr: false, // HMR does not work for our Web Components atm + liveReload: true + }, + server: { + port: 9000 // TODO: Leave default in production + }, + source: { + decorators: { + version: "legacy" + } + }, + resolve: { + alias: { + // Webpack fallbacks for Node.js modules + vm: false, + // Workspace aliases for proper resolution + "@openremote/core": "../../component/core/src", + "@openremote/model": "../../component/model/src", + "@openremote/rest": "../../component/rest/src", + "@openremote/or-app": "../../component/or-app/src", + "@openremote/or-asset-tree": "../../component/or-asset-tree/src", + "@openremote/or-asset-viewer": "../../component/or-asset-viewer/src", + "@openremote/or-attribute-barchart": "../../component/or-attribute-barchart/src", + "@openremote/or-attribute-card": "../../component/or-attribute-card/src", + "@openremote/or-attribute-history": "../../component/or-attribute-history/src", + "@openremote/or-attribute-input": "../../component/or-attribute-input/src", + "@openremote/or-attribute-picker": "../../component/or-attribute-picker/src", + "@openremote/or-components": "../../component/or-components/src", + "@openremote/or-chart": "../../component/or-chart/src", + "@openremote/or-dashboard-builder": "../../component/or-dashboard-builder/src", + "@openremote/or-gauge": "../../component/or-gauge/src", + "@openremote/or-icon": "../../component/or-icon/src", + "@openremote/or-json-forms": "../../component/or-json-forms/src", + "@openremote/or-log-viewer": "../../component/or-log-viewer/src", + "@openremote/or-map": "../../component/or-map/src", + "@openremote/or-mwc-components": "../../component/or-mwc-components/src", + "@openremote/or-rules": "../../component/or-rules/src", + "@openremote/or-services": "../../component/or-services/src", + "@openremote/or-translate": "../../component/or-translate/src", + "@openremote/or-tree-menu": "../../component/or-tree-menu/src", + "@openremote/or-vaadin-components": "../../component/or-vaadin-components/src", + "@openremote/theme": "../../component/theme", + "@openremote/util": "../../util" + } + }, + plugins: [], + tools: { + bundlerChain: (chain, { CHAIN_ID }) => { + chain.module.rule(CHAIN_ID.RULE.CSS).exclude.add(/(@material|@mdi).*\.css$/); // Exclude the external CSS imports from default bundling. + }, + rspack: (config, { addRules }) => { + addRules([{test: /(@material|@mdi).*\.css$/, type: "asset/source"}]); // Add rule to treat external CSS imports as raw strings. + return config; + } + } +}); 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 ce2de17161..d77bb417d4 100644 --- a/ui/app/manager/src/components/configuration/or-conf-json.ts +++ b/ui/app/manager/src/components/configuration/or-conf-json.ts @@ -25,7 +25,6 @@ import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-di import { createRef, Ref, ref } from "lit/directives/ref.js"; import { OrAceEditor, OrAceEditorChangedEvent } from "@openremote/or-components/or-ace-editor"; import { ManagerAppConfig, MapConfig } from "@openremote/model"; -import { InputType } from "@openremote/or-mwc-components/or-mwc-input"; @customElement("or-conf-json") export class OrConfJson extends LitElement { @@ -118,7 +117,10 @@ export class OrConfJson extends LitElement { render() { return html` - + this._showConfigDialog()}> + + + ` } } 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 2e147df48e..34e6a9d252 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 @@ -164,7 +164,7 @@ export class OrConfMapCard extends LitElement { padding: 10px 0; } - .input or-mwc-input:not([icon]) { + .input or-vaadin-number-field { width: 80%; } @@ -212,29 +212,29 @@ export class OrConfMapCard extends LitElement {
${i18next.t("configuration.mapBounds")}
${i18next.t("configuration.mapBoundsDescription")} - - - - + this.setBoundary(3, (ev.currentTarget as HTMLInputElement).value)}> + + + this.setBoundary(2, (ev.currentTarget as HTMLInputElement).value)}> + + + this.setBoundary(1, (ev.currentTarget as HTMLInputElement).value)}> + + + this.setBoundary(0, (ev.currentTarget as HTMLInputElement).value)}> + + +
${i18next.t("configuration.center")}
${i18next.t("configuration.centerDescription")} - + this.setCenter((ev.currentTarget as HTMLInputElement).value)}> +
${i18next.t("configuration.geoJson")}
${i18next.t("configuration.geoJsonDescription")}
@@ -250,43 +250,42 @@ export class OrConfMapCard extends LitElement {
${i18next.t("configuration.mapZoom")}
${i18next.t("configuration.mapZoomDescription")}
- - + { + map.zoom = Number((ev.currentTarget as HTMLInputElement).value); + this.notifyConfigChange(map); + }}> + + + this.setZoom(map.zoom)}> + +
- - + { + map.minZoom = Number((ev.currentTarget as HTMLInputElement).value); + this.requestUpdate(); + this.notifyConfigChange(map); + }}> + + + this.setZoom(map.minZoom)}> + +
- - + { + map.maxZoom = Number((ev.currentTarget as HTMLInputElement).value); + this.requestUpdate("map"); + this.notifyConfigChange(map); + }}> + + + this.setZoom(map.maxZoom)}> + +
@@ -301,10 +300,9 @@ export class OrConfMapCard extends LitElement {
${when(this.canRemove, () => html` - + this._showRemoveMapDialog()}> + + `)}
` } 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 d733c60a20..89815516ec 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 @@ -4,7 +4,6 @@ import { html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import "@openremote/or-components/or-ace-editor"; import { OrAceEditorChangedEvent } from "@openremote/or-components/or-ace-editor"; -import { InputType } from "@openremote/or-mwc-components/or-mwc-input"; @customElement("or-conf-map-geojson") export class OrConfMapGeoJson extends LitElement { @@ -22,7 +21,12 @@ export class OrConfMapGeoJson extends LitElement { /* -------------- */ protected render() { - return html`` + return html` + this.showDialog()}> + + + + `; } protected showDialog() { diff --git a/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-global.ts b/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-global.ts index 275034042a..cf3fdb4624 100644 --- a/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-global.ts +++ b/ui/app/manager/src/components/configuration/or-conf-map/or-conf-map-global.ts @@ -108,17 +108,16 @@ export class OrConfMapGlobal extends LitElement {
- + { + const newValue = (ev.currentTarget as HTMLInputElement).value; + this.config.sources.vector_tiles.tiles = newValue ? [newValue] : undefined; + this.config.sources.vector_tiles.custom = !!newValue; + this.notifyConfigChange(this.config); + }}> +
@@ -152,17 +151,15 @@ export class OrConfMapGlobal extends LitElement {
- + { + this.config.override = (ev.currentTarget as HTMLInputElement).value || undefined + this.notifyConfigChange(this.config) + this.requestUpdate(); + }}> +
@@ -172,10 +169,11 @@ export class OrConfMapGlobal extends LitElement {
- + this.importMapSettings()}> + + + ${when(hasExternalSource, () => html` - + this.resetMapSettings()}> + + + `)}
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 12e7a948f3..099abafc02 100644 --- a/ui/app/manager/src/components/configuration/or-conf-panel.ts +++ b/ui/app/manager/src/components/configuration/or-conf-panel.ts @@ -10,6 +10,7 @@ import "./or-conf-map/or-conf-map-card"; import "./or-conf-realm/or-conf-realm-card"; import {OrConfRealmCard} from "./or-conf-realm/or-conf-realm-card"; import {OrConfMapCard} from "./or-conf-map/or-conf-map-card"; +import {OrVaadinSelect} from "@openremote/or-vaadin-components/or-vaadin-select"; @customElement("or-conf-panel") export class OrConfPanel extends LitElement { @@ -97,9 +98,10 @@ export class OrConfPanel extends LitElement {
${when(availableRealms.length > 0, () => html` - + this._showAddingRealmDialog()}> + + + `)}
` @@ -174,15 +176,17 @@ export class OrConfPanel extends LitElement { }, ]; const headingKey = this.isMapConfig(this.config) ? 'configuration.addMapCustomization' : 'configuration.addRealmCustomization'; + const realmItems = this.getAvailableRealms(this.config, this.realmOptions).map(item => ({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` - + attributePickerModalOpen()}> + + `; } @@ -881,9 +907,9 @@ export function getPropertyTemplate(asset: Asset, property: string, hostElement: (names) => { if (names) { if (hostElement && hostElement.shadowRoot) { - const pathField = hostElement.shadowRoot.getElementById("property-parentId") as OrMwcInput; + const pathField = hostElement.shadowRoot.getElementById("property-parentId") as OrVaadinInput; if (pathField) { - pathField.value = names.join(" > "); + pathField.setAttribute("value", names.join(" > ")); } } } @@ -900,7 +926,12 @@ export function getPropertyTemplate(asset: Asset, property: string, hostElement: break; } - return html``; + return html` + + `; } export function getField(name: string, itemConfig?: InfoPanelItemConfig, content?: TemplateResult): TemplateResult { @@ -1020,6 +1051,7 @@ export async function saveAsset(asset: Asset): Promise { if (!asset.id) { throw new Error("Request to update existing asset but asset ID is not set"); } + console.debug("Updating asset to", asset); const response = await manager.rest.api.AssetResource.update(asset.id!, asset); success = response.status === 200; savedAsset = response.data; @@ -1155,10 +1187,10 @@ export class OrAssetViewer extends subscribe(manager)(translate(i18next)(LitElem protected wrapperElem!: HTMLDivElement; @query("#save-btn") - protected saveBtnElem!: OrMwcInput; + protected saveBtnElem!: OrVaadinButton; @query("#edit-btn") - protected editBtnElem!: OrMwcInput; + protected editBtnElem!: OrVaadinButton; @query("#editor") protected editor!: OrEditAssetPanel; @@ -1383,17 +1415,35 @@ export class OrAssetViewer extends subscribe(manager)(translate(i18next)(LitElem
- ${editMode - ? html` - - ` - : html`${asset.name}`} + ${when(editMode, () => html` + { + const elem = ev.currentTarget as OrVaadinTextField; + if(elem.checkValidity()) { + asset!.name = elem.value; + this._assetInfo!.modified = true; + this._doValidation(); + } + }}> + + + `, () => html` + ${asset.name} + `)}
${validationErrors.length === 0 ? (asset!.createdOn ? html`` : ``) : html``} - ${editMode ? html`` : ``} - ${!this._isReadonly() ? html` - `: ``} + ${when(editMode, () => html` + this._onSaveClicked()}> + + + `)} + ${when(!this._isReadonly(), () => html` + this._onEditToggleClicked(!this.editMode)}> + + + + `)}
${content} @@ -1416,8 +1466,6 @@ export class OrAssetViewer extends subscribe(manager)(translate(i18next)(LitElem (detail) => { if (detail.allow) { this._doEditToggle(edit); - } else { - this.editBtnElem.value = !edit; } }); } @@ -1471,6 +1519,7 @@ export class OrAssetViewer extends subscribe(manager)(translate(i18next)(LitElem protected _onAssetModified(validationResults: ValidatorResult[]) { if (this._assetInfo) { + console.debug("Changing modified state to TRUE!") this._assetInfo.modified = true; this._validationResults = validationResults; } 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 e41a2f20f1..22699eeab9 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 @@ -1,10 +1,12 @@ import {html, LitElement, css, PropertyValues} from "lit"; -import {customElement, property, query} from "lit/decorators.js"; -import {InputType, OrMwcInput, OrInputChangedEvent} from "@openremote/or-mwc-components/or-mwc-input"; +import {customElement, property} from "lit/decorators.js"; +import {OrVaadinTextField} from "@openremote/or-vaadin-components/or-vaadin-text-field"; +import {OrVaadinComboBox} from "@openremote/or-vaadin-components/or-vaadin-combo-box"; +import {OrVaadinCheckbox} from "@openremote/or-vaadin-components/or-vaadin-checkbox"; +import {OrVaadinNumberField} from "@openremote/or-vaadin-components/or-vaadin-number-field"; import {i18next} from "@openremote/or-translate" import {Asset, Attribute, AssetModelUtil} from "@openremote/model"; import {Util} from "@openremote/core"; -import "@openremote/or-mwc-components/or-mwc-input"; export class OrAddAttributePanelAttributeChangedEvent extends CustomEvent> { @@ -42,21 +44,21 @@ export class OrAddAttributePanel extends LitElement { @property() public arrayDimensions: number = 1; - @query("#array-input") - protected arrayInput!: OrMwcInput; - protected customAttribute: boolean = true; protected attributeTypes?: [string, string][]; protected attributeValueTypes?: [string, string][]; protected arrayRegex: RegExp = /\[\]/g; public static get styles() { - return css` - or-mwc-input { - width: 300px; - display: block; - padding: 10px 20px; - }`; + return css` + #attribute-creator { + min-width: 300px; + padding: 20px; + display: flex; + flex-direction: column; + gap: 10px; + } + `; } protected shouldUpdate(_changedProperties: PropertyValues) { @@ -99,14 +101,34 @@ export class OrAddAttributePanel extends LitElement { if (!this.attributeTypes || !this.attributeValueTypes) { return; } + const nameItems = this.attributeTypes.map(type => ({key: type[0], value: type[1]})); + const nameSelected = nameItems.find(n => n.key === this.attribute.name) ?? nameItems[0]; + const typeItems = this.attributeValueTypes.map(type => ({key: type[0], value: type[1]})); + const typeSelected = typeItems.find(i => i.key === this.attribute?.type?.replace(this.arrayRegex, "")); return html`
- - - - - + this.onTypeChanged((ev.currentTarget as OrVaadinComboBox).value)}> + + + this.onNameChanged((ev.currentTarget as OrVaadinTextField).value)}> + + + 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 daa3894a54..c23a9ca394 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 @@ -1,32 +1,23 @@ import {css, html, LitElement, PropertyValues, TemplateResult, unsafeCSS} from "lit"; import {until} from "lit/directives/until.js"; -import {customElement, property, state} from "lit/decorators.js"; -import { - InputType, - OrMwcInput, - OrInputChangedEvent, - getValueHolderInputTemplateProvider, - ValueInputProviderOptions, - OrInputChangedEventDetail, - ValueInputProvider, - ValueInputProviderGenerator -} from "@openremote/or-mwc-components/or-mwc-input"; +import {customElement, property} from "lit/decorators.js"; +import {type ValueInputProviderOptions, getValueHolderInputTemplateProvider} from "@openremote/or-vaadin-components/value-input-provider"; +import {InputType, OrMwcInput, OrInputChangedEvent} from "@openremote/or-mwc-components/or-mwc-input"; import {i18next} from "@openremote/or-translate" import {Asset, Attribute, NameValueHolder, AssetModelUtil, WellknownMetaItems} from "@openremote/model"; -import { DefaultColor5, DefaultColor3, DefaultColor2, Util} from "@openremote/core"; +import {DefaultColor5, DefaultColor3, Util} from "@openremote/core"; import "@openremote/or-mwc-components/or-mwc-input"; import {OrIcon} from "@openremote/or-icon"; import {showDialog, OrMwcDialog, DialogAction} from "@openremote/or-mwc-components/or-mwc-dialog"; import {ListItem, ListType, OrMwcList} from "@openremote/or-mwc-components/or-mwc-list"; import "./or-add-attribute-panel"; import {getField, getPanel, getPropertyTemplate} from "./index"; -import { - OrAddAttributePanelAttributeChangedEvent, -} from "./or-add-attribute-panel"; +import {OrAddAttributePanelAttributeChangedEvent} from "./or-add-attribute-panel"; import {panelStyles} from "./style"; -import { OrAssetTree, UiAssetTreeNode } from "@openremote/or-asset-tree"; -import {jsonFormsInputTemplateProvider, OrAttributeInput, OrAttributeInputChangedEvent } from "@openremote/or-attribute-input"; -import {createRef, ref, Ref } from "lit/directives/ref.js"; +import {OrAssetTree, UiAssetTreeNode} from "@openremote/or-asset-tree"; +import {jsonFormsInputTemplateProvider, OrAttributeInput, OrAttributeInputChangedEvent} from "@openremote/or-attribute-input"; +import {createRef, ref, Ref} from "lit/directives/ref.js"; +import {when} from "lit/directives/when.js"; // TODO: Add webpack/rollup to build so consumers aren't forced to use the same tooling const tableStyle = require("@material/data-table/dist/mdc.data-table.css"); @@ -40,7 +31,7 @@ const style = css` #parent-edit-wrapper { display: flex; - align-items: center; + align-items: end; } #parent-edit-wrapper > #property-parentId { @@ -107,7 +98,7 @@ const style = css` .attribute-meta-row.expanded .meta-item-container { transition: max-height 0.5s ease-in-out; } - .meta-item-container or-mwc-input { + .meta-item-container or-mwc-input, .meta-item-container or-vaadin-input { width: 100%; } .meta-item-wrapper { @@ -237,7 +228,7 @@ export class OrEditAssetPanel extends LitElement { const properties: TemplateResult[] = [ getField("type", undefined, getPropertyTemplate(this.asset, "type", this, undefined, undefined, {readonly: true, label: i18next.t("assetType")})), getField("parent", undefined, this._getParentTemplate()), - html`
+ html`
${getField("accessPublicRead", undefined, getPropertyTemplate(this.asset, "accessPublicRead", this, undefined, undefined, {readonly: false, label: i18next.t("accessPublicRead")}))}
` ]; @@ -290,7 +281,7 @@ export class OrEditAssetPanel extends LitElement { expandToggle(tdElems[i] as HTMLElement); } } - tdElem.setAttribute("label", this.expandedAll ? "expandAll" : "collapseAll"); + tdElem.setAttribute("value", this.expandedAll ? "expandAll" : "collapseAll"); this.expandedAll = !this.expandedAll; } }; @@ -311,7 +302,11 @@ export class OrEditAssetPanel extends LitElement { - + + + + + @@ -319,7 +314,10 @@ export class OrEditAssetPanel extends LitElement {
- + this._addAttribute()}> + + +
@@ -376,7 +374,12 @@ export class OrEditAssetPanel extends LitElement { disableWrite disableSubscribe disableButton compact @or-attribute-input-changed="${(e: OrAttributeInputChangedEvent) => this._onAttributeModified(attribute, e.detail.value)}"> - ${canDelete ? html`` : ``} + + ${when(canDelete, () => html` + + + + `)} @@ -386,7 +389,10 @@ export class OrEditAssetPanel extends LitElement { ${metaTemplatesAndValidators.map((metaTemplateAndValidator) => metaTemplateAndValidator.template)}
- + this._addMetaItems(attribute)}> + + +
@@ -409,6 +415,7 @@ export class OrEditAssetPanel extends LitElement { } protected _onAttributeModified(attribute: Attribute, newValue: any) { + console.debug("Detected attribute modification;", attribute, newValue); // Check if modification came from external change const index = this.changedAttributes.indexOf(attribute.name!); @@ -422,8 +429,9 @@ export class OrEditAssetPanel extends LitElement { this._onModified(); } - protected _onMetaItemModified(attribute: Attribute, metaItem: NameValueHolder, detail: OrInputChangedEventDetail | undefined) { - metaItem.value = detail ? detail.value : undefined; + protected _onMetaItemModified(attribute: Attribute, metaItem: NameValueHolder, value: any) { + console.debug("Detected Meta item modification;", attribute, metaItem); + metaItem.value = value ?? undefined; attribute.meta![metaItem.name!] = metaItem.value; this._onModified(); } @@ -441,14 +449,14 @@ export class OrEditAssetPanel extends LitElement { if (!valueDescriptor) { console.log("Couldn't find value descriptor for meta item so falling back to simple JSON input: " + metaItem.name); - content = html``; + content = html``; } else { const options: ValueInputProviderOptions = { label: Util.getMetaLabel(metaItem, descriptor!, this.asset.type!, true), resizeVertical: true }; - const standardInputProvider = getValueHolderInputTemplateProvider(this.asset.type!, metaItem, descriptor, valueDescriptor, (detail) => this._onMetaItemModified(attribute, metaItem, detail), options); - let provider = jsonFormsInputTemplateProvider(standardInputProvider)(this.asset.type!, metaItem, descriptor, valueDescriptor, (detail) => this._onMetaItemModified(attribute, metaItem, detail), options); + const standardInputProvider = getValueHolderInputTemplateProvider(this.asset.type!, metaItem, descriptor, valueDescriptor, (value: any) => this._onMetaItemModified(attribute, metaItem, value), options); + let provider = jsonFormsInputTemplateProvider(standardInputProvider)(this.asset.type!, metaItem, descriptor, valueDescriptor, (detail) => this._onMetaItemModified(attribute, metaItem, detail?.value), options); if (!provider) { provider = standardInputProvider; @@ -700,7 +708,9 @@ export class OrEditAssetPanel extends LitElement { return html`
${getPropertyTemplate(this.asset, "parentId", this, undefined, undefined, {readonly: true, label: i18next.t("parent")})} - + + +
`; } diff --git a/ui/component/or-asset-viewer/src/style.ts b/ui/component/or-asset-viewer/src/style.ts index fe871d1df8..76e0288ec8 100644 --- a/ui/component/or-asset-viewer/src/style.ts +++ b/ui/component/or-asset-viewer/src/style.ts @@ -35,13 +35,11 @@ export const panelStyles = css` } .panel-title { - text-transform: uppercase; - font-weight: bolder; - line-height: 1em; + font-size: 18px; + font-weight: bold; color: var(--internal-or-asset-viewer-title-text-color); margin-bottom: 20px; flex: 0 0 auto; - letter-spacing: 0.025em; } .field { @@ -259,9 +257,10 @@ export const style = css` #fileupload, #discovery { display: flex; - align-items: center; + align-items: end; width: 100%; margin-bottom: 10px; + gap: 10px; } .hidden { diff --git a/ui/component/or-asset-viewer/test/asset-viewer.test.ts b/ui/component/or-asset-viewer/test/asset-viewer.test.ts index 90d6c76d34..4626be832f 100644 --- a/ui/component/or-asset-viewer/test/asset-viewer.test.ts +++ b/ui/component/or-asset-viewer/test/asset-viewer.test.ts @@ -34,7 +34,7 @@ ct("Should show asset invalid error", async ({ mount, assetViewer }) => { }); await assetViewer.getAttributeValueLocator("invalid").fill("0.1"); await assetViewer.getAttributeValueLocator("invalid").press("Enter"); - await expect(component).toContainText("Asset is not valid"); + await expect(assetViewer.getAttributeValueLocator("invalid")).toHaveAttribute("invalid") }); ct("Should not show asset invalid error after switching assets", async ({ mount, assetViewer }) => { @@ -43,7 +43,7 @@ ct("Should not show asset invalid error after switching assets", async ({ mount, }); await assetViewer.getAttributeValueLocator("invalid").fill("0.1"); await assetViewer.getAttributeValueLocator("invalid").press("Enter"); - await expect(component).toContainText("Asset is not valid"); + await expect(assetViewer.getAttributeValueLocator("invalid")).toHaveAttribute("invalid") await component.update({ props: { assetId: validId, editMode: true } }); await expect(component).not.toContainText("Asset is not valid"); diff --git a/ui/component/or-asset-viewer/tsconfig.json b/ui/component/or-asset-viewer/tsconfig.json index 1b805cbb8f..2cfad82060 100644 --- a/ui/component/or-asset-viewer/tsconfig.json +++ b/ui/component/or-asset-viewer/tsconfig.json @@ -19,6 +19,7 @@ { "path": "../or-attribute-input" }, { "path": "../or-attribute-history" }, { "path": "../or-mwc-components" }, - { "path": "../or-json-forms" } + { "path": "../or-json-forms" }, + { "path": "../or-vaadin-components" } ] } diff --git a/ui/component/or-attribute-barchart/package.json b/ui/component/or-attribute-barchart/package.json index fdfab2cacb..d751a87016 100644 --- a/ui/component/or-attribute-barchart/package.json +++ b/ui/component/or-attribute-barchart/package.json @@ -23,6 +23,7 @@ "@openremote/or-icon": "workspace:*", "@openremote/or-mwc-components": "workspace:*", "@openremote/or-translate": "workspace:*", + "@openremote/or-vaadin-components": "workspace:*", "@openremote/rest": "workspace:*", "echarts": "~6.0.0", "lit": "^3.3.1", diff --git a/ui/component/or-attribute-barchart/src/index.ts b/ui/component/or-attribute-barchart/src/index.ts index e8da271bdb..dc3d505f23 100644 --- a/ui/component/or-attribute-barchart/src/index.ts +++ b/ui/component/or-attribute-barchart/src/index.ts @@ -36,7 +36,6 @@ import {DatasetComponentOption, DataZoomComponent, DataZoomComponentOption, Grid import {BarChart, BarSeriesOption} from "echarts/charts"; import {CanvasRenderer} from "echarts/renderers"; import {UniversalTransition} from "echarts/features"; -import {InputType, OrMwcInput} from "@openremote/or-mwc-components/or-mwc-input"; import moment from "moment"; import {getAssetDescriptorIconTemplate} from "@openremote/or-icon"; import {isAxiosError} from "@openremote/rest"; @@ -44,8 +43,8 @@ import {OrMwcDialog, showDialog} from "@openremote/or-mwc-components/or-mwc-dial import {showSnackbar} from "@openremote/or-mwc-components/or-mwc-snackbar"; import {cache} from "lit/directives/cache.js"; import "@openremote/or-mwc-components/or-mwc-menu"; -import {getContentWithMenuTemplate} from "@openremote/or-mwc-components/or-mwc-menu"; -import {ListItem} from "@openremote/or-mwc-components/or-mwc-list"; +import {createMenuBarItem, MenuBarItem} from "@openremote/or-vaadin-components/or-vaadin-menu-bar"; +import {OrVaadinDateTimePicker} from "@openremote/or-vaadin-components/or-vaadin-date-time-picker"; import {when} from "lit/directives/when.js"; import {createRef, Ref, ref} from "lit/directives/ref.js"; import "@openremote/or-translate"; @@ -235,21 +234,6 @@ const style = css` margin-bottom: 5px; } - .dialog-container { - display: flex; - flex-direction: row; - flex: 1 1 0; - } - - .dialog-container > * { - flex: 1 1 0; - } - - .dialog-container > or-mwc-input { - background-color: var(--or-app-color2); - border-left: 3px solid var(--or-app-color4); - } - #chart-container { flex: 1 1 0; position: relative; @@ -603,15 +587,7 @@ export class OrAttributeBarChart extends LitElement { ${when(this.timestampControls || this.showLegend, () => html`
- ${when(this.timestampControls && this.timePrefixKey && this.timePrefixOptions && this.timeWindowKey && this.timeWindowOptions, - () => this._getTimeControlsTemplate(disabled || !!this.timeframe), - () => html` -
- - -
- ` - )} + ${when(this.timestampControls && this.timePrefixKey && this.timeWindowKey, () => this._getTimeControlsTemplate(disabled || !!this.timeframe))} ${when(this.timeframe, () => this._getTimeframeStatusTemplate(this.timeframe))}
${cache(when(this.showLegend, () => this._getLegendTemplate()))} @@ -621,49 +597,37 @@ export class OrAttributeBarChart extends LitElement { `; } - protected _getTimeControlsTemplate(disabled = true): TemplateResult { + protected _getTimeControlsTemplate(disabled: boolean): TemplateResult { + console.debug("Disabled?", disabled); + const menuItems: MenuBarItem[] = [{ + component: createMenuBarItem(html``), + children: this.timePrefixOptions.map(o => ( + {type: "prefix", value: o, component: createMenuBarItem(html``)} + )) + }, { + component: createMenuBarItem(html``), + children: Array.from(this.timeWindowOptions).map(o => ( + {type: "window", value: o[0], component: createMenuBarItem(html``)} + )) + }]; + const intervalMenuItems: MenuBarItem[] = [{ + component: createMenuBarItem(html``), + children: Array.from(this.intervalOptions.keys()).map(o => ( + {value: o, component: createMenuBarItem(html``)} + )) + }] return html`
+
- - ${getContentWithMenuTemplate( - html``, - disabled ? null as any : this.timePrefixOptions.map(option => ({value: option} as ListItem)), - this.timePrefixKey, - (value: string | string[]) => { - this.timeframe = undefined; // remove any custom start & end times - this.timePrefixKey = value.toString(); - }, - undefined, - undefined, - undefined, - true - )} - - ${getContentWithMenuTemplate( - html``, - disabled ? null as any : Array.from(this.timeWindowOptions.keys()).map(key => ({value: key, text: key.toLowerCase(), translate: true} as ListItem)), - this.timeWindowKey, - (value: string | string[]) => { - this.timeframe = undefined; // remove any custom start & end times - this.timeWindowKey = value.toString(); - }, - undefined, - undefined, - undefined, - true - )} + this._onTimeframeMenuSelect(ev)} + >
- ${getContentWithMenuTemplate(html``, - Array.from(this.intervalOptions.keys()).map(key => ({value: key, text: `intervalBy${key.toLowerCase()}`, translate: true} as ListItem)), - this.interval, - value => this.interval = value as BarChartInterval, - undefined, - undefined, - undefined, - true - )} + this.interval = ev.detail.value.value} + >