@openremote/or-json-forms 1.8.0-snapshot.20250725074716 → 1.8.0-snapshot.20250725120000

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/util.js CHANGED
@@ -1,23 +1,291 @@
1
- import{composeWithUi as e,createDefaultValue as t,deriveTypes as r,getAjv as o,getData as i,getRenderers as n,getSchema as s,hasShowRule as a,isVisible as c,Resolve as l}from"@jsonforms/core";import{DefaultColor5 as m,Util as p}from"@openremote/core";import{InputType as d}from"@openremote/or-mwc-components/or-mwc-input";import{i18next as f}from"@openremote/or-translate";import"@openremote/or-components/or-ace-editor";import{html as u,unsafeCSS as h}from"lit";import{createRef as b,ref as v}from"lit/directives/ref.js";import{unknownTemplate as S}from"./standard-renderers";import{OrMwcDialog as g,showDialog as y}from"@openremote/or-mwc-components/or-mwc-dialog";export function getTemplateFromProps(e,t){let r;if(!e||!t)return u``;let o=t.renderers||n({jsonforms:Object.assign({},e)}),i=t.schema,s=t.uischema;if(o&&i&&s&&e.core){let n=o.map(t=>[t,t.tester(s,i,{rootSchema:resolveSubSchemasRecursive(i,e.core.schema),config:e.config})]).sort((e,t)=>t[1]-e[1]),a=n&&n.length>0?n[0]:void 0;r=a&&-1!==a[1]?a[0].renderer(e,t):S()}return r}export function getCombinatorInfos(e,o){return e.map(e=>{let i,n,s,a=findSchemaTitleAndDescription(e,o);if(e.$ref&&(e=l.schema(e,"",o)),Array.isArray(e.allOf)&&(e=l.schema(e,"allOf",o)),r(e).every(e=>"object"===e)){let r=getSchemaObjectProperties(e).find(([e,t])=>void 0!==getSchemaConst(t));r?(i=r[0],n=getSchemaConst(r[1]),s=()=>{let e={};return e[r[0]]=getSchemaConst(r[1]),e},a[0]||(a[0]=getSchemaConst(r[1]))):s=()=>t(e,o)}else s=()=>t(e,o);return{title:a[0],description:a[1],defaultValueCreator:s,constProperty:i,constValue:n}})}export function getSchemaConst(e){if(e){if(void 0!==e.const)return e.const;if(Array.isArray(e.enum)&&1===e.enum.length)return e.enum[0]}}export function getSchemaPicker(e,t,r,o,i,n){let s=getCombinatorInfos(t[o],e),a=s.map((e,t)=>[t+"",e.title||f.t("schema.title.indexedItem",{index:t})]),c=i?f.t("schema.anyOfPickerLabel",{label:i}):f.t("type");return u`
2
- <or-mwc-input class="any-of-picker" .label="${c}" .type="${d.SELECT}" .options="${a}" @or-mwc-input-changed="${e=>{n(s[Number(e.detail.value)])}}"></or-mwc-input>
3
- `}export function findSchemaTitleAndDescription(e,t){let r;if(e.$ref&&(r=getLabelFromScopeOrRef(e.$ref),e=l.schema(e,"",t)),e.title)return[e.title,e.description];if(e.allOf){let r=l.schema(e,"allOf",t).allOf.find(e=>!!e.title);if(r)return[r.title,r.description]}return[f.t("schema.title."+r,{defaultValue:p.camelCaseToSentenceCase(r)}),void 0]}function getLabelFromScopeOrRef(e){return e.substr(e.lastIndexOf("/")+1)}function getSchemaObjectProperties(e){let t=[];return e.allOf?t=e.allOf.map(e=>e.properties?Object.entries(e.properties):[]).flat():e.properties&&(t=Object.entries(e.properties)),t}export function mapStateToCombinatorRendererProps(t,r,n){let{uischema:m}=r,p=e(m,r.path),d=s(t),f=l.schema(r.schema||d,m.scope,d),u=void 0===r.visible||a(m)?c(m,i(t),r.path,o(t)):r.visible,h=r.id,b=l.data(i(t),p),v=t.jsonforms.core.ajv,S=f||d,g=l.schema(S,n,d),y=["required","additionalProperties","type","enum","const"],O=e=>!e||0===e.length||!e.find(e=>-1!==y.indexOf(e.keyword)),w=-1;for(let e=0;e<g.length;e++)try{let t=Object.assign({definitions:d.definitions},g[e]),r=v.compile(t);if(r(b),O(r.errors)){w=e;break}}catch(e){console.debug("Combinator subschema is not self contained, can't hand it over to AJV")}return{data:b,path:p,schema:S,rootSchema:d,visible:u,id:h,indexOfFittingSchema:w,uischemas:t.jsonforms.uischemas,uischema:m}}export function getLabel(e,t,r,o){if(r)return r;let i=findSchemaTitleAndDescription(e,t);return i[0]?i[0]:o?p.camelCaseToSentenceCase(getLabelFromScopeOrRef(o)):void 0}export function resolveSubSchemasRecursive(e,t,r){return e.$ref?resolveSubSchemasRecursive(l.schema(t,e.$ref,t),t):((r?[r]:["allOf","anyOf","oneOf"]).forEach(r=>{let o=e[r];o&&(e[r]=o.map(e=>resolveSubSchemasRecursive(e,t)))}),e.items&&(Array.isArray(e.items)?e.items=e.items.map(e=>resolveSubSchemasRecursive(e,t)):e.items=resolveSubSchemasRecursive(e.items,t)),e.properties&&Object.keys(e.properties).forEach(r=>e.properties[r]=resolveSubSchemasRecursive(e.properties[r],t)),e)}export const controlWithoutLabel=e=>({type:"Control",scope:e,label:!1});export const showJsonEditor=(e,t,r)=>{let o=b(),i=b();y(new g().setContent(u`
4
- <or-ace-editor ${v(o)} @or-ace-editor-edit="${()=>void(i.value.disabled=!0)}" @or-ace-editor-changed="${e=>(e=>{let t=e.detail.valid;i.value.disabled=!t})(e)}" .value="${t}"></or-ace-editor>
5
- `).setActions([{actionName:"cancel",content:"cancel"},{default:!0,actionName:"update",action:()=>{let e=o.value;e.validate()&&r(e.getValue()?JSON.parse(e.getValue()):void 0)},content:u`<or-mwc-input ${v(i)} disabled .type="${d.BUTTON}" label="update"></or-mwc-input>`}]).setHeading(e).setDismissAction(null).setStyles(u`
6
- <style>
7
- .mdc-dialog__surface {
8
- width: 1024px;
9
- overflow-x: visible !important;
10
- overflow-y: visible !important;
11
- }
12
- #dialog-content {
13
- border-color: var(--or-app-color5, ${h(m)});
14
- border-top-width: 1px;
15
- border-top-style: solid;
16
- border-bottom-width: 1px;
17
- border-bottom-style: solid;
18
- padding: 0;
19
- overflow: visible;
20
- height: 60vh;
21
- }
22
- </style>
23
- `))};
1
+ import { composeWithUi, createDefaultValue, deriveTypes, getAjv, getData, getRenderers, getSchema, hasShowRule, isVisible, Resolve, } from "@jsonforms/core";
2
+ import { DefaultColor5, Util } from "@openremote/core";
3
+ import { InputType } from "@openremote/or-mwc-components/or-mwc-input";
4
+ import { i18next } from "@openremote/or-translate";
5
+ import "@openremote/or-components/or-ace-editor";
6
+ import { html, unsafeCSS } from "lit";
7
+ import { createRef, ref } from 'lit/directives/ref.js';
8
+ import { unknownTemplate } from "./standard-renderers";
9
+ import { OrMwcDialog, showDialog } from "@openremote/or-mwc-components/or-mwc-dialog";
10
+ export function getTemplateFromProps(state, props) {
11
+ if (!state || !props) {
12
+ return html ``;
13
+ }
14
+ const renderers = props.renderers || getRenderers({ jsonforms: Object.assign({}, state) });
15
+ const schema = props.schema;
16
+ const uischema = props.uischema;
17
+ let template;
18
+ if (renderers && schema && uischema && state.core) {
19
+ const orderedRenderers = renderers.map(r => [r, r.tester(uischema, schema, { rootSchema: resolveSubSchemasRecursive(schema, state.core.schema), config: state.config })]).sort((a, b) => b[1] - a[1]);
20
+ const renderer = orderedRenderers && orderedRenderers.length > 0 ? orderedRenderers[0] : undefined;
21
+ if (renderer && renderer[1] !== -1) {
22
+ template = renderer[0].renderer(state, props);
23
+ }
24
+ else {
25
+ template = unknownTemplate();
26
+ }
27
+ }
28
+ return template;
29
+ }
30
+ /**
31
+ * For a given anyOf schema array this will try and extract a common const property which can be used as a discriminator
32
+ * when creating instances
33
+ */
34
+ export function getCombinatorInfos(schemas, rootSchema) {
35
+ return schemas.map(schema => {
36
+ let constProperty;
37
+ let constValue;
38
+ let creator;
39
+ const titleAndDescription = findSchemaTitleAndDescription(schema, rootSchema);
40
+ if (schema.$ref) {
41
+ schema = Resolve.schema(schema, '', rootSchema);
42
+ }
43
+ if (Array.isArray(schema.allOf)) {
44
+ schema = Resolve.schema(schema, "allOf", rootSchema);
45
+ }
46
+ if (deriveTypes(schema).every(type => type === "object")) {
47
+ const props = getSchemaObjectProperties(schema);
48
+ const constProp = props.find(([propName, propSchema]) => getSchemaConst(propSchema) !== undefined);
49
+ if (constProp) {
50
+ constProperty = constProp[0];
51
+ constValue = getSchemaConst(constProp[1]);
52
+ creator = () => {
53
+ const obj = {};
54
+ obj[constProp[0]] = getSchemaConst(constProp[1]);
55
+ return obj;
56
+ };
57
+ if (!titleAndDescription[0]) {
58
+ titleAndDescription[0] = getSchemaConst(constProp[1]);
59
+ }
60
+ }
61
+ else {
62
+ creator = () => createDefaultValue(schema, rootSchema);
63
+ }
64
+ }
65
+ else {
66
+ // Assume a primitive type that can be instantiated with default value creator
67
+ creator = () => createDefaultValue(schema, rootSchema);
68
+ }
69
+ return {
70
+ title: titleAndDescription[0],
71
+ description: titleAndDescription[1],
72
+ defaultValueCreator: creator,
73
+ constProperty: constProperty,
74
+ constValue: constValue
75
+ };
76
+ });
77
+ }
78
+ export function getSchemaConst(schema) {
79
+ if (!schema) {
80
+ return;
81
+ }
82
+ if (schema.const !== undefined) {
83
+ return schema.const;
84
+ }
85
+ if (Array.isArray(schema.enum) && schema.enum.length === 1) {
86
+ return schema.enum[0];
87
+ }
88
+ }
89
+ export function getSchemaPicker(rootSchema, resolvedSchema, path, keyword, label, selectedCallback) {
90
+ const combinatorInfos = getCombinatorInfos(resolvedSchema[keyword], rootSchema);
91
+ const options = combinatorInfos.map((combinatorInfo, index) => [index + "", combinatorInfo.title || i18next.t("schema.title.indexedItem", { index: index })]);
92
+ const pickerUpdater = (index) => {
93
+ const matchedInfo = combinatorInfos[index];
94
+ selectedCallback(matchedInfo);
95
+ };
96
+ const pickerLabel = label ? i18next.t("schema.anyOfPickerLabel", { label: label }) : i18next.t("type");
97
+ return html `
98
+ <or-mwc-input class="any-of-picker" .label="${pickerLabel}" .type="${InputType.SELECT}" .options="${options}" @or-mwc-input-changed="${(ev) => pickerUpdater(Number(ev.detail.value))}"></or-mwc-input>
99
+ `;
100
+ }
101
+ // TODO: use i18n translations from jsonforms/core
102
+ export function findSchemaTitleAndDescription(schema, rootSchema) {
103
+ let title;
104
+ if (schema.$ref) {
105
+ title = getLabelFromScopeOrRef(schema.$ref);
106
+ schema = Resolve.schema(schema, '', rootSchema);
107
+ }
108
+ if (schema.title) {
109
+ return [schema.title, schema.description];
110
+ }
111
+ if (schema.allOf) {
112
+ const resolvedSchema = Resolve.schema(schema, "allOf", rootSchema);
113
+ const titledSchema = resolvedSchema.allOf.find((allOfSchema) => {
114
+ return !!allOfSchema.title;
115
+ });
116
+ if (titledSchema) {
117
+ return [titledSchema.title, titledSchema.description];
118
+ }
119
+ }
120
+ return [i18next.t("schema.title." + title, { defaultValue: Util.camelCaseToSentenceCase(title) }), undefined];
121
+ }
122
+ function getLabelFromScopeOrRef(scopeOrRef) {
123
+ return scopeOrRef.substr(scopeOrRef.lastIndexOf("/") + 1);
124
+ }
125
+ function getSchemaObjectProperties(schema) {
126
+ let props = [];
127
+ if (schema.allOf) {
128
+ props = schema.allOf.map(schema => schema.properties ? Object.entries(schema.properties) : []).flat();
129
+ }
130
+ else if (schema.properties) {
131
+ props = Object.entries(schema.properties);
132
+ }
133
+ return props;
134
+ }
135
+ /**
136
+ * Copied from eclipse source code to inject global definitions into the validating schema otherwise AJV will fail
137
+ * to compile the schema - not perfect but works for our cases
138
+ */
139
+ export function mapStateToCombinatorRendererProps(state, ownProps, keyword) {
140
+ const { uischema } = ownProps;
141
+ const path = composeWithUi(uischema, ownProps.path);
142
+ const rootSchema = getSchema(state);
143
+ const resolvedSchema = Resolve.schema(ownProps.schema || rootSchema, uischema.scope, rootSchema);
144
+ const visible = ownProps.visible === undefined || hasShowRule(uischema)
145
+ ? isVisible(uischema, getData(state), ownProps.path, getAjv(state))
146
+ : ownProps.visible;
147
+ const id = ownProps.id;
148
+ const data = Resolve.data(getData(state), path);
149
+ const ajv = state.jsonforms.core.ajv;
150
+ const schema = resolvedSchema || rootSchema;
151
+ const _schema = Resolve.schema(schema, keyword, rootSchema);
152
+ const structuralKeywords = [
153
+ 'required',
154
+ 'additionalProperties',
155
+ 'type',
156
+ 'enum',
157
+ 'const'
158
+ ];
159
+ const dataIsValid = (errors) => {
160
+ return (!errors ||
161
+ errors.length === 0 ||
162
+ !errors.find(e => structuralKeywords.indexOf(e.keyword) !== -1));
163
+ };
164
+ let indexOfFittingSchema = -1;
165
+ // TODO instead of compiling the combinator subschemas we can compile the original schema
166
+ // without the combinator alternatives and then revalidate and check the errors for the
167
+ // element
168
+ for (let i = 0; i < _schema.length; i++) {
169
+ try {
170
+ const schema = Object.assign({ definitions: rootSchema.definitions }, _schema[i]);
171
+ const valFn = ajv.compile(schema);
172
+ valFn(data);
173
+ if (dataIsValid(valFn.errors)) {
174
+ indexOfFittingSchema = i;
175
+ break;
176
+ }
177
+ }
178
+ catch (error) {
179
+ console.debug("Combinator subschema is not self contained, can't hand it over to AJV");
180
+ }
181
+ }
182
+ return {
183
+ data,
184
+ path,
185
+ schema,
186
+ rootSchema,
187
+ visible,
188
+ id,
189
+ indexOfFittingSchema,
190
+ uischemas: state.jsonforms.uischemas,
191
+ uischema: uischema,
192
+ };
193
+ }
194
+ export function getLabel(schema, rootSchema, uiElementLabel, uiElementScope) {
195
+ if (uiElementLabel) {
196
+ return uiElementLabel;
197
+ }
198
+ const titleAndDesc = findSchemaTitleAndDescription(schema, rootSchema);
199
+ if (titleAndDesc[0]) {
200
+ return titleAndDesc[0];
201
+ }
202
+ if (uiElementScope) {
203
+ return Util.camelCaseToSentenceCase(getLabelFromScopeOrRef(uiElementScope));
204
+ }
205
+ return undefined;
206
+ }
207
+ export function resolveSubSchemasRecursive(schema, rootSchema, keyword) {
208
+ const combinators = keyword ? [keyword] : ["allOf", "anyOf", "oneOf"];
209
+ if (schema.$ref) {
210
+ return resolveSubSchemasRecursive(Resolve.schema(rootSchema, schema.$ref, rootSchema), rootSchema);
211
+ }
212
+ combinators.forEach((combinator) => {
213
+ const schemas = schema[combinator];
214
+ if (schemas) {
215
+ schema[combinator] = schemas.map(subSchema => resolveSubSchemasRecursive(subSchema, rootSchema));
216
+ }
217
+ });
218
+ if (schema.items) {
219
+ if (Array.isArray(schema.items)) {
220
+ schema.items = schema.items.map((itemSchema) => resolveSubSchemasRecursive(itemSchema, rootSchema));
221
+ }
222
+ else {
223
+ schema.items = resolveSubSchemasRecursive(schema.items, rootSchema);
224
+ }
225
+ }
226
+ if (schema.properties) {
227
+ Object.keys(schema.properties).forEach((prop) => schema.properties[prop] = resolveSubSchemasRecursive(schema.properties[prop], rootSchema));
228
+ }
229
+ return schema;
230
+ }
231
+ export const controlWithoutLabel = (scope) => ({
232
+ type: 'Control',
233
+ scope: scope,
234
+ label: false
235
+ });
236
+ export const showJsonEditor = (title, value, updateCallback) => {
237
+ const editorRef = createRef();
238
+ const updateBtnRef = createRef();
239
+ const onEditorEdit = () => {
240
+ // Disable update button whilst edit in progress
241
+ updateBtnRef.value.disabled = true;
242
+ };
243
+ const onEditorChanged = (ev) => {
244
+ const valid = ev.detail.valid;
245
+ updateBtnRef.value.disabled = !valid;
246
+ };
247
+ const dialog = showDialog(new OrMwcDialog()
248
+ .setContent(html `
249
+ <or-ace-editor ${ref(editorRef)} @or-ace-editor-edit="${() => onEditorEdit()}" @or-ace-editor-changed="${(ev) => onEditorChanged(ev)}" .value="${value}"></or-ace-editor>
250
+ `)
251
+ .setActions([
252
+ {
253
+ actionName: "cancel",
254
+ content: "cancel"
255
+ },
256
+ {
257
+ default: true,
258
+ actionName: "update",
259
+ action: () => {
260
+ const editor = editorRef.value;
261
+ if (editor.validate()) {
262
+ const data = !!editor.getValue() ? JSON.parse(editor.getValue()) : undefined;
263
+ updateCallback(data);
264
+ }
265
+ },
266
+ content: html `<or-mwc-input ${ref(updateBtnRef)} disabled .type="${InputType.BUTTON}" label="update"></or-mwc-input>`
267
+ }
268
+ ])
269
+ .setHeading(title)
270
+ .setDismissAction(null)
271
+ .setStyles(html `
272
+ <style>
273
+ .mdc-dialog__surface {
274
+ width: 1024px;
275
+ overflow-x: visible !important;
276
+ overflow-y: visible !important;
277
+ }
278
+ #dialog-content {
279
+ border-color: var(--or-app-color5, ${unsafeCSS(DefaultColor5)});
280
+ border-top-width: 1px;
281
+ border-top-style: solid;
282
+ border-bottom-width: 1px;
283
+ border-bottom-style: solid;
284
+ padding: 0;
285
+ overflow: visible;
286
+ height: 60vh;
287
+ }
288
+ </style>
289
+ `));
290
+ };
291
+ //# sourceMappingURL=util.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openremote/or-json-forms",
3
- "version": "1.8.0-snapshot.20250725074716",
3
+ "version": "1.8.0-snapshot.20250725120000",
4
4
  "description": "Renders a JSON schema and provides instance editing",
5
5
  "customElements": "custom-elements.json",
6
6
  "main": "dist/umd/index.bundle.js",
@@ -20,14 +20,14 @@
20
20
  "license": "AGPL-3.0-or-later",
21
21
  "dependencies": {
22
22
  "@jsonforms/core": "^3.5.1",
23
- "@openremote/or-components": "1.8.0-snapshot.20250725074716",
24
- "@openremote/or-mwc-components": "1.8.0-snapshot.20250725074716",
23
+ "@openremote/or-components": "1.8.0-snapshot.20250725120000",
24
+ "@openremote/or-mwc-components": "1.8.0-snapshot.20250725120000",
25
25
  "@polymer/polymer": "^3.3.1",
26
26
  "ajv": "^8.8.2",
27
27
  "lit": "^2.0.2"
28
28
  },
29
29
  "devDependencies": {
30
- "@openremote/util": "1.8.0-snapshot.20250725074716"
30
+ "@openremote/util": "1.8.0-snapshot.20250725120000"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"