Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Pick the approach by how much of the base component you need to keep. The base +

### Registering an input type in the input pipeline

To make an input type usable through the input pipeline, register it in `or-vaadin-input.ts` (`TEMPLATES` map + a `getXTemplate`, and `nativeValue` if it is a boolean that exposes `checked` instead of `value`) and update `value-input-provider.ts` (boolean/checked types use the `checked` attribute, not `value`). Where the Vaadin component represents the value differently than the attribute does, such as the date time picker taking a local ISO string instead of a timestamp, convert the value and any min/max there and convert back through `valueConverter`.
To make an input type usable through the input pipeline, register it in `or-vaadin-input.ts` (`TEMPLATES` map + a `getXTemplate`) and update `value-input-provider.ts`. Where the Vaadin component represents the value differently than the attribute does, convert at both ends: `nativeValue` and `checkValidity` read and validate what the component holds, while each consumer serializes the value and any min/max into the attributes and converts back through `valueConverter`. Booleans expose `checked` instead of `value`, JSON types carry their value as formatted text, and the date time picker takes a local ISO string instead of a timestamp.

## Storybook

Expand Down
1 change: 1 addition & 0 deletions ui/app/shared/locales/en/or.json
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@
"addItem": "Add item",
"array": "Array",
"agentId": "Agent ID",
"selectAgent": "Select agent",
"agentNotFound": "Agent not found",
"agentTypeMismatch": "Agent type mismatch",
"loadFailedRoles": "Failed to load roles",
Expand Down
1 change: 1 addition & 0 deletions ui/app/shared/locales/nl/or.json
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@
"addItem": "Voeg item toe",
"array": "Array",
"agentId": "Agent ID",
"selectAgent": "Selecteer agent",
"agentNotFound": "Agent niet gevonden",
"agentTypeMismatch": "Agent type mismatch",
"loadFailedRoles": "Rollen ophalen mislukt",
Expand Down
30 changes: 18 additions & 12 deletions ui/component/or-attribute-input/src/renderers/agent-link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ import {
uiTypeIs,
formatIs,
} from "@openremote/or-json-forms";
import { InputType, type OrInputChangedEvent } from "@openremote/or-mwc-components/or-mwc-input";
import type { SelectItem } from "@openremote/or-vaadin-components/or-vaadin-select";
import type { OrVaadinComboBox } from "@openremote/or-vaadin-components/or-vaadin-combo-box";
import { html } from "lit";
import "@openremote/or-mwc-components/or-mwc-input";
import "@openremote/or-vaadin-components/or-vaadin-combo-box";
import { i18next } from "@openremote/or-translate";
import { until } from "lit/directives/until.js";

Expand Down Expand Up @@ -118,19 +119,24 @@ const agentIdRenderer = (state: JsonFormsStateContext, props: ControlProps) => {
};

const loadedTemplatePromise = loadAgents().then((agents) => {
const options: [string, string][] = agents.map((agent) => [agent.id!, agent.name + " (" + agent.id + ")"]);
const options: SelectItem[] = agents.map((agent) => ({
value: agent.id!,
label: agent.name + " (" + agent.id + ")",
}));

// A combo box rather than a select, so the list can be filtered and the agent id copied out of the field
return html`
<or-mwc-input
.label="${i18next.t("agentId")}"
<or-vaadin-combo-box
required
class="agent-id-picker"
@or-mwc-input-changed="${(ev: OrInputChangedEvent) => onAgentChanged(agents.find((agent) => agent.id === ev.detail.value))}"
type="${InputType.SELECT}"
.value="${props.data}"
.placeholder="${i18next.t("selectAgent")}"
.options="${options}"
></or-mwc-input>
@change="${(ev: Event) =>
onAgentChanged(agents.find((agent) => agent.id === (ev.currentTarget as OrVaadinComboBox).value))}"
placeholder="${i18next.t("selectAgent")}"
.items="${options}"
.value="${props.data ?? ""}"
>
<or-translate slot="label" value="agentId"></or-translate>
</or-vaadin-combo-box>
`;
});

Expand All @@ -142,7 +148,7 @@ const agentIdRenderer = (state: JsonFormsStateContext, props: ControlProps) => {
width: 100%;
}
</style>
${until(loadedTemplatePromise, html`<or-mwc-input class="agent-id-picker" .type="${InputType.SELECT}"></or-mwc-input>`)}
${until(loadedTemplatePromise, html`<or-vaadin-combo-box class="agent-id-picker"></or-vaadin-combo-box>`)}
`;

return getTemplateWrapper(template, undefined);
Expand Down
1 change: 0 additions & 1 deletion ui/component/or-json-forms/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"@openremote/core": "workspace:*",
"@openremote/or-components": "workspace:*",
"@openremote/or-element": "workspace:*",
"@openremote/or-mwc-components": "workspace:*",
"@openremote/or-translate": "workspace:*",
"@openremote/or-vaadin-components": "workspace:*",
"ajv": "^8.20.0",
Expand Down
101 changes: 50 additions & 51 deletions ui/component/or-json-forms/src/controls/control-array-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ import {
showJsonEditor,
} from "../util";
import { i18next } from "@openremote/or-translate";
import { OrMwcDialog, showDialog } 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 { addItemOrParameterDialogStyle, baseStyle, panelStyle } from "../styles";
import type { ListItem, OrMwcListChangedEvent } from "@openremote/or-mwc-components/or-mwc-list";
import type { ListItem } from "@openremote/or-vaadin-components/or-vaadin-list-box";
import type { OrVaadinButton } from "@openremote/or-vaadin-components/or-vaadin-button";
import { createRef, type Ref, ref } from "lit/directives/ref.js";
import "@openremote/or-vaadin-components/or-vaadin-dialog";
import "@openremote/or-vaadin-components/or-vaadin-list-box";
import "@openremote/or-vaadin-components/or-vaadin-item";
import "@openremote/or-vaadin-components/or-vaadin-button";
import { DefaultColor4, DefaultColor5 } from "@openremote/core";
import { ControlBaseElement } from "./control-base-element";
Expand Down Expand Up @@ -133,7 +136,7 @@ export class ControlArrayElement extends ControlBaseElement {
protected moveItem!: (fromIndex: number, toIndex: number) => void;

public static get styles() {
return [baseStyle, panelStyle, style];
return [baseStyle, panelStyle, addItemOrParameterDialogStyle, style];
}

shouldUpdate(_changedProperties: PropertyValues): boolean {
Expand Down Expand Up @@ -319,7 +322,7 @@ export class ControlArrayElement extends ControlBaseElement {
protected _showJson(ev: Event) {
ev.stopPropagation();

showJsonEditor(this.title || this.schema.title || "", this.data, (newValue) => {
showJsonEditor(this.shadowRoot!, this.title || this.schema.title || "", this.data, (newValue) => {
this.handleChange(this.path || "", newValue);
});
}
Expand All @@ -338,65 +341,61 @@ export class ControlArrayElement extends ControlBaseElement {

protected showAddDialog() {
let selectedItemInfo: CombinatorInfo | undefined;
let dialog: OrVaadinDialog | undefined;
const descRef: Ref<HTMLDivElement> = createRef();
const addBtnRef: Ref<OrVaadinButton> = createRef();

const listItems: ListItem[] = this.itemInfos!.map((itemInfo, index) => {
const listItems: ListItem[] = this.itemInfos!.map((itemInfo) => {
const labelStr = itemInfo.title ? computeLabel(itemInfo.title, false, true) : "";
return {
text: labelStr,
value: labelStr,
data: itemInfo,
};
});
}).sort((a, b) => a.text!.localeCompare(b.text!));

const onParamChanged = (itemInfo: CombinatorInfo) => {
selectedItemInfo = itemInfo;
const descElem = dialog.shadowRoot!.getElementById("parameter-desc") as HTMLDivElement;
descElem.innerHTML = itemInfo.description || "";
(dialog.shadowRoot!.getElementById("add-btn") as OrVaadinButton).disabled = false;
descRef.value!.innerHTML = itemInfo.description || "";
addBtnRef.value!.disabled = false;
};

const onAdd = () => {
if (selectedItemInfo) {
this.addItem(selectedItemInfo.defaultValueCreator());
}
dialog?.close();
};

const dialog = showDialog(
new OrMwcDialog()
.setContent(html`
<div class="col">
<form id="mdc-dialog-form-add" class="row">
<div id="type-list" class="col">
<or-mwc-list
@or-mwc-list-changed="${(evt: OrMwcListChangedEvent) => {
if (evt.detail.length === 1) onParamChanged((evt.detail[0] as ListItem).data as CombinatorInfo);
}}"
.listItems="${listItems.sort((a, b) => a.text!.localeCompare(b.text!))}"
id="parameter-list"
></or-mwc-list>
</div>
<div id="parameter-desc" class="col"></div>
</form>
dialog = showDialog(
this.shadowRoot!,
html`
<or-vaadin-dialog width="800px">
Comment thread
Ekhorn marked this conversation as resolved.
<h2 slot="header-content">
${(this.label ? computeLabel(this.label, this.required, false) + " - " : "") + i18next.t("addItem")}
</h2>
<div id="dialog-content">
<or-vaadin-list-box
id="type-list"
@selected-changed="${(ev: CustomEvent) => {
const selected = listItems[ev.detail.value as number];
if (selected) onParamChanged(selected.data as CombinatorInfo);
}}"
>
${listItems.map((item) => html`<or-vaadin-item>${item.text}</or-vaadin-item>`)}
</or-vaadin-list-box>
<div id="parameter-desc" ${ref(descRef)}></div>
</div>
`)
.setStyles(addItemOrParameterDialogStyle)
.setHeading((this.label ? computeLabel(this.label, this.required, false) + " - " : "") + i18next.t("addItem"))
.setActions([
{
actionName: "cancel",
content: "cancel",
},
{
default: true,
actionName: "add",
action: () => {
if (selectedItemInfo) {
const value = selectedItemInfo.defaultValueCreator();
this.addItem(value);
}
},
content: html`
<or-vaadin-button id="add-btn" disabled>
<or-translate value="add"></or-translate>
</or-vaadin-button>
`,
},
])
.setDismissAction(null)
<div id="dialog-footer" slot="footer">
<or-vaadin-button theme="tertiary" @click="${() => dialog?.close()}">
<or-translate value="cancel"></or-translate>
</or-vaadin-button>
<or-vaadin-button ${ref(addBtnRef)} theme="primary" disabled @click="${() => onAdd()}">
<or-translate value="add"></or-translate>
</or-vaadin-button>
</div>
</or-vaadin-dialog>
`
);
}
}
Loading
Loading