diff --git a/packages/core/src/mappers/renderer.ts b/packages/core/src/mappers/renderer.ts
index f67730334..47493be0f 100644
--- a/packages/core/src/mappers/renderer.ts
+++ b/packages/core/src/mappers/renderer.ts
@@ -592,11 +592,12 @@ export const mapStateToControlProps = (
const required =
controlElement.scope !== undefined &&
isRequired(ownProps.schema, controlElement.scope, rootSchema);
- const resolvedSchema = Resolve.schema(
- ownProps.schema || rootSchema,
- controlElement.scope,
- rootSchema
- );
+ const resolvedSchema =
+ Resolve.schema(
+ ownProps.schema || rootSchema,
+ controlElement.scope,
+ rootSchema
+ ) ?? ownProps.schema; // if resolve fails then rely that the ownProps.schema if exist
const errors = getErrorAt(path, resolvedSchema)(state);
const description =
@@ -871,7 +872,21 @@ export const mapStateToArrayControlProps = (
const { path, schema, uischema, label, ...props } =
mapStateToControlWithDetailProps(state, ownProps);
- const resolvedSchema = Resolve.schema(schema, 'items', props.rootSchema);
+ let resolvedSchema = Resolve.schema(schema, 'items', props.rootSchema);
+ if ((resolvedSchema as any) === true) {
+ // help the testers to determine the mixed control
+ resolvedSchema = {
+ type: [
+ 'array',
+ 'boolean',
+ 'integer',
+ 'null',
+ 'number',
+ 'object',
+ 'string',
+ ],
+ };
+ }
const childErrors = getSubErrorsAt(path, resolvedSchema)(state);
return {
diff --git a/packages/core/src/util/resolvers.ts b/packages/core/src/util/resolvers.ts
index 92880f6a2..2e903270c 100644
--- a/packages/core/src/util/resolvers.ts
+++ b/packages/core/src/util/resolvers.ts
@@ -123,6 +123,14 @@ const resolveSchemaWithSegments = (
pathSegments: string[],
rootSchema: JsonSchema
): JsonSchema => {
+ if (
+ typeof schema === 'boolean' &&
+ (!pathSegments || pathSegments.length === 0)
+ ) {
+ // add the case where the schema can be true value for example: items: true or additionalProperties: false
+ return schema;
+ }
+
if (isEmpty(schema)) {
return undefined;
}
diff --git a/packages/examples/src/examples/json-editor.ts b/packages/examples/src/examples/json-editor.ts
new file mode 100644
index 000000000..6366e5ae7
--- /dev/null
+++ b/packages/examples/src/examples/json-editor.ts
@@ -0,0 +1,50 @@
+/*
+ The MIT License
+
+ Copyright (c) 2017-2019 EclipseSource Munich
+ https://github.com/eclipsesource/jsonforms
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+import { registerExamples } from '../register';
+
+export const schema = {
+ type: ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'],
+ additionalProperties: true,
+ items: {
+ type: ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string'],
+ },
+};
+
+export const uischema = {
+ type: 'Control',
+ scope: '#/',
+};
+
+const data = undefined as any;
+
+registerExamples([
+ {
+ name: 'json-editor',
+ label: 'JSON Editor',
+ data,
+ schema,
+ uischema,
+ },
+]);
diff --git a/packages/examples/src/examples/jsonschema.ts b/packages/examples/src/examples/jsonschema.ts
new file mode 100644
index 000000000..79c37c6d2
--- /dev/null
+++ b/packages/examples/src/examples/jsonschema.ts
@@ -0,0 +1,592 @@
+/*
+ The MIT License
+
+ Copyright (c) 2017-2019 EclipseSource Munich
+ https://github.com/eclipsesource/jsonforms
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+import {
+ type JsonFormsUISchemaRegistryEntry,
+ type JsonSchema,
+ NOT_APPLICABLE,
+ type UISchemaElement,
+} from '@jsonforms/core';
+import { registerExamples } from '../register';
+import { StateProps } from '../example';
+
+export const schema = {
+ $schema: 'http://json-schema.org/draft-07/schema#',
+ $id: 'http://json-schema.org/draft-07/schema#',
+ title: 'Core schema meta-schema',
+ definitions: {
+ schemaArray: {
+ type: 'array',
+ minItems: 1,
+ items: { $ref: '#' },
+ },
+ nonNegativeInteger: {
+ type: 'integer',
+ minimum: 0,
+ },
+ nonNegativeIntegerDefault0: {
+ allOf: [{ $ref: '#/definitions/nonNegativeInteger' }, { default: 0 }],
+ },
+ simpleTypes: {
+ type: 'string',
+ enum: [
+ 'array',
+ 'boolean',
+ 'integer',
+ 'null',
+ 'number',
+ 'object',
+ 'string',
+ ],
+ },
+ stringArray: {
+ type: 'array',
+ items: { type: 'string' },
+ uniqueItems: true,
+ default: [] as any,
+ },
+ },
+ type: ['object', 'boolean'],
+ properties: {
+ $id: {
+ type: 'string',
+ format: 'uri-reference',
+ },
+ $schema: {
+ type: 'string',
+ format: 'uri',
+ },
+ $ref: {
+ type: 'string',
+ format: 'uri-reference',
+ },
+ $comment: {
+ type: 'string',
+ },
+ title: {
+ type: 'string',
+ },
+ description: {
+ type: 'string',
+ },
+ default: true,
+ readOnly: {
+ type: 'boolean',
+ default: false,
+ },
+ writeOnly: {
+ type: 'boolean',
+ default: false,
+ },
+ examples: {
+ type: 'array',
+ items: true,
+ },
+ multipleOf: {
+ type: 'number',
+ exclusiveMinimum: 0,
+ },
+ maximum: {
+ type: 'number',
+ },
+ exclusiveMaximum: {
+ type: 'number',
+ },
+ minimum: {
+ type: 'number',
+ },
+ exclusiveMinimum: {
+ type: 'number',
+ },
+ maxLength: { $ref: '#/definitions/nonNegativeInteger' },
+ minLength: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
+ pattern: {
+ type: 'string',
+ format: 'regex',
+ },
+ additionalItems: { $ref: '#' },
+ items: {
+ anyOf: [{ $ref: '#' }, { $ref: '#/definitions/schemaArray' }],
+ default: true,
+ },
+ maxItems: { $ref: '#/definitions/nonNegativeInteger' },
+ minItems: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
+ uniqueItems: {
+ type: 'boolean',
+ default: false,
+ },
+ contains: { $ref: '#' },
+ maxProperties: { $ref: '#/definitions/nonNegativeInteger' },
+ minProperties: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
+ required: { $ref: '#/definitions/stringArray' },
+ additionalProperties: { $ref: '#' },
+ definitions: {
+ type: 'object',
+ additionalProperties: { $ref: '#' },
+ default: {},
+ },
+ properties: {
+ type: 'object',
+ additionalProperties: { $ref: '#' },
+ default: {},
+ },
+ patternProperties: {
+ type: 'object',
+ additionalProperties: { $ref: '#' },
+ propertyNames: { format: 'regex' },
+ default: {},
+ },
+ dependencies: {
+ type: 'object',
+ additionalProperties: {
+ anyOf: [{ $ref: '#' }, { $ref: '#/definitions/stringArray' }],
+ },
+ },
+ propertyNames: { $ref: '#' },
+ const: true,
+ enum: {
+ type: 'array',
+ items: true,
+ minItems: 1,
+ uniqueItems: true,
+ },
+ type: {
+ anyOf: [
+ { $ref: '#/definitions/simpleTypes' },
+ {
+ type: 'array',
+ items: { $ref: '#/definitions/simpleTypes' },
+ minItems: 1,
+ uniqueItems: true,
+ },
+ ],
+ },
+ format: { type: 'string' },
+ contentMediaType: { type: 'string' },
+ contentEncoding: { type: 'string' },
+ if: { $ref: '#' },
+ then: { $ref: '#' },
+ else: { $ref: '#' },
+ allOf: { $ref: '#/definitions/schemaArray' },
+ anyOf: { $ref: '#/definitions/schemaArray' },
+ oneOf: { $ref: '#/definitions/schemaArray' },
+ not: { $ref: '#' },
+ },
+ default: true,
+};
+
+export const uischema: UISchemaElement = undefined as any as UISchemaElement;
+
+export const data = {
+ type: 'object',
+ properties: {
+ name: {
+ type: 'string',
+ minLength: 3,
+ description: 'Please enter your name',
+ },
+ vegetarian: {
+ type: 'boolean',
+ },
+ birthDate: {
+ type: 'string',
+ format: 'date',
+ },
+ nationality: {
+ type: 'string',
+ enum: ['DE', 'IT', 'JP', 'US', 'RU', 'Other'],
+ },
+ personalData: {
+ type: 'object',
+ properties: {
+ age: {
+ type: 'integer',
+ description: 'Please enter your age.',
+ },
+ height: {
+ type: 'number',
+ },
+ drivingSkill: {
+ type: 'number',
+ maximum: 10,
+ minimum: 1,
+ default: 7,
+ },
+ },
+ required: ['age', 'height'],
+ },
+ occupation: {
+ type: 'string',
+ },
+ postalCode: {
+ type: 'string',
+ maxLength: 5,
+ },
+ },
+ required: ['occupation', 'nationality'],
+};
+
+const shouldContainTypeCondition = (type: string[]) => {
+ return {
+ scope: '#/properties/type',
+ schema: {
+ anyOf: [
+ {
+ type: 'string',
+ enum: type,
+ },
+ {
+ type: 'array',
+ items: {
+ type: 'string',
+ },
+ contains: {
+ type: 'string',
+ enum: type,
+ },
+ },
+ ],
+ },
+ };
+};
+
+const typeIsSpecifiedCondition = {
+ scope: '#/properties/type',
+ schema: {
+ oneOf: [
+ {
+ type: 'string',
+ enum: [
+ 'string',
+ 'number',
+ 'integer',
+ 'array',
+ 'object',
+ 'boolean',
+ 'null',
+ ],
+ },
+ {
+ type: 'array',
+ items: {
+ type: 'string',
+ enum: [
+ 'string',
+ 'number',
+ 'integer',
+ 'array',
+ 'object',
+ 'boolean',
+ 'null',
+ ],
+ },
+ minItems: 1,
+ uniqueItems: true,
+ },
+ ],
+ },
+};
+
+const properties = {
+ type: 'Control',
+ scope: '#/properties/properties',
+};
+
+const required = {
+ type: 'Control',
+ scope: '#/properties/required',
+};
+
+const constraintsLayout = {
+ type: 'Categorization',
+ elements: [
+ {
+ type: 'Category',
+ label: 'Number',
+ rule: {
+ effect: 'SHOW',
+ condition: shouldContainTypeCondition(['number', 'integer']),
+ },
+ elements: [
+ {
+ type: 'VerticalLayout',
+ elements: [
+ {
+ type: 'Control',
+ scope: '#/properties/multipleOf',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/maximum',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/exclusiveMaximum',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/minimum',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/exclusiveMinimum',
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: 'Category',
+ label: 'String',
+ rule: {
+ effect: 'SHOW',
+ condition: shouldContainTypeCondition(['string']),
+ },
+ elements: [
+ {
+ type: 'Control',
+ scope: '#/properties/maxLength',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/minLength',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/pattern',
+ },
+ ],
+ },
+ {
+ type: 'Category',
+ label: 'Array',
+ rule: {
+ effect: 'SHOW',
+ condition: shouldContainTypeCondition(['array']),
+ },
+ elements: [
+ {
+ type: 'Control',
+ scope: '#/properties/minItems',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/maxItems',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/uniqueItems',
+ },
+ ],
+ },
+ {
+ type: 'Category',
+ label: 'Object',
+ rule: {
+ effect: 'SHOW',
+ condition: shouldContainTypeCondition(['object']),
+ },
+ elements: [
+ {
+ type: 'Control',
+ scope: '#/properties/minProperties',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/maxProperties',
+ },
+ ],
+ },
+ ],
+};
+
+const controlLabel = (
+ controlSchemaPath: string,
+ controlLabel: string,
+ controlSchemaType: string | undefined = undefined
+) => {
+ return {
+ tester: (jsonSchema: JsonSchema, schemaPath: string, _path: string) => {
+ if (
+ controlSchemaPath === schemaPath &&
+ (controlSchemaType === undefined ||
+ controlSchemaType === jsonSchema.type)
+ ) {
+ return 2;
+ }
+ return NOT_APPLICABLE;
+ },
+ uischema: {
+ type: 'Control',
+ scope: '#',
+ label: controlLabel,
+ },
+ };
+};
+
+export const uischemas = [
+ controlLabel('#/properties/minItems', 'Min Items', 'number'),
+ controlLabel('#/properties/maxItems', 'Max Items'),
+ {
+ tester: (jsonSchema: JsonSchema, schemaPath: string, path: string) => {
+ if (
+ path === 'type' &&
+ schemaPath === '#/properties/type' &&
+ jsonSchema.type === 'array'
+ ) {
+ return 2;
+ }
+ return NOT_APPLICABLE;
+ },
+ uischema: {
+ type: 'Control',
+ scope: '#',
+ },
+ },
+ {
+ tester: (jsonSchema: JsonSchema, _schemaPath: string, _path: string) => {
+ return 'http://json-schema.org/draft-07/schema#' ===
+ (jsonSchema as any).$id && jsonSchema.type === 'object'
+ ? 2
+ : NOT_APPLICABLE;
+ },
+ uischema: {
+ type: 'Categorization',
+ elements: [
+ {
+ type: 'Category',
+ label: 'Basic',
+ elements: [
+ {
+ type: 'VerticalLayout',
+ elements: [
+ { type: 'Control', scope: '#/properties/$id' },
+ { type: 'Control', scope: '#/properties/$schema' },
+ { type: 'Control', scope: '#/properties/title' },
+ { type: 'Control', scope: '#/properties/description' },
+ ],
+ },
+ ],
+ },
+ {
+ type: 'Category',
+ label: 'Structure',
+ elements: [
+ {
+ type: 'VerticalLayout',
+ elements: [
+ {
+ type: 'Control',
+ scope: '#/properties/type',
+ options: {
+ 'label-0': 'Single Type',
+ 'label-1': 'Multiple Types',
+ },
+ },
+ {
+ type: 'Categorization',
+ rule: {
+ effect: 'SHOW',
+ condition: shouldContainTypeCondition(['object']),
+ },
+ elements: [
+ {
+ type: 'Category',
+ label: 'Properties',
+ elements: [properties],
+ },
+ {
+ type: 'Category',
+ label: 'Required',
+ elements: [required],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: 'Category',
+ label: 'Constraints',
+ rule: {
+ effect: 'SHOW',
+ condition: typeIsSpecifiedCondition,
+ },
+ elements: [constraintsLayout],
+ },
+ ],
+ options: {
+ variant: 'stepper',
+ showNavButtons: true,
+ },
+ },
+ },
+ {
+ tester: (jsonSchema: JsonSchema, _schemaPath: string, _path: string) => {
+ return 'http://json-schema.org/draft-07/schema#' ===
+ (jsonSchema as any).$id && jsonSchema.type === 'boolean'
+ ? 2
+ : NOT_APPLICABLE;
+ },
+ uischema: {
+ type: 'VerticalLayout',
+ elements: [{ type: 'Control', scope: '#/' }],
+ },
+ },
+];
+
+const actions = [
+ {
+ label: 'Register UISchema',
+ apply: (props: StateProps) => {
+ return {
+ ...props,
+ uischemas: uischemas,
+ };
+ },
+ },
+ {
+ label: 'Unregister UISchema',
+ apply: (props: StateProps) => {
+ const uischemas: JsonFormsUISchemaRegistryEntry[] =
+ undefined as any as JsonFormsUISchemaRegistryEntry[];
+ return {
+ ...props,
+ uischemas: uischemas,
+ };
+ },
+ },
+];
+
+registerExamples([
+ {
+ name: 'jsonschema',
+ label: 'JsonSchema',
+ data,
+ schema: schema as any as JsonSchema,
+ uischema,
+ actions,
+ },
+]);
diff --git a/packages/examples/src/examples/mixed-object.ts b/packages/examples/src/examples/mixed-object.ts
new file mode 100644
index 000000000..8e41e7971
--- /dev/null
+++ b/packages/examples/src/examples/mixed-object.ts
@@ -0,0 +1,85 @@
+/*
+ The MIT License
+
+ Copyright (c) 2017-2019 EclipseSource Munich
+ https://github.com/eclipsesource/jsonforms
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+import { registerExamples } from '../register';
+
+export const schema = {
+ $schema: 'http://json-schema.org/draft-07/schema#',
+ title: 'Mixed Types Example',
+ type: 'object',
+ properties: {
+ mixedSimple: {
+ type: ['string', 'boolean', 'integer'],
+ description: 'This property can be a string, boolean, or integer.',
+ },
+ nullableString: {
+ type: ['string', 'null'],
+ },
+ mixed: {
+ type: [
+ 'array',
+ 'boolean',
+ 'integer',
+ 'null',
+ 'number',
+ 'object',
+ 'string',
+ ],
+ },
+ },
+ required: ['mixedSimple'],
+};
+
+export const uischema = {
+ type: 'VerticalLayout',
+ elements: [
+ {
+ type: 'Control',
+ scope: '#/properties/mixedSimple',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/nullableString',
+ },
+ {
+ type: 'Control',
+ scope: '#/properties/mixed',
+ },
+ ],
+};
+
+const data = {
+ mixedSimple: 'String',
+ nullableString: null as any,
+};
+
+registerExamples([
+ {
+ name: 'mixed-object',
+ label: 'Mixed Object',
+ data,
+ schema,
+ uischema,
+ },
+]);
diff --git a/packages/examples/src/examples/mixed.ts b/packages/examples/src/examples/mixed.ts
new file mode 100644
index 000000000..1de6590a1
--- /dev/null
+++ b/packages/examples/src/examples/mixed.ts
@@ -0,0 +1,47 @@
+/*
+ The MIT License
+
+ Copyright (c) 2017-2019 EclipseSource Munich
+ https://github.com/eclipsesource/jsonforms
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+import { registerExamples } from '../register';
+
+export const schema = {
+ $schema: 'http://json-schema.org/draft-07/schema#',
+ type: ['string', 'boolean', 'integer', 'null'],
+};
+
+export const uischema = {
+ type: 'Control',
+ scope: '#/',
+};
+
+const data = undefined as any;
+
+registerExamples([
+ {
+ name: 'mixed',
+ label: 'Mixed',
+ data,
+ schema,
+ uischema,
+ },
+]);
diff --git a/packages/examples/src/index.ts b/packages/examples/src/index.ts
index c50414362..cb8684ed4 100644
--- a/packages/examples/src/index.ts
+++ b/packages/examples/src/index.ts
@@ -75,11 +75,15 @@ import * as additionalErrors from './examples/additional-errors';
import * as multiEnumWithLabelAndDesc from './examples/enum-multi-with-label-and-desc';
import * as additionalProperties from './examples/additional-properties';
import * as login from './examples/login';
+import * as mixed from './examples/mixed';
+import * as mixedObject from './examples/mixed-object';
import * as string from './examples/string';
export * from './register';
export * from './example';
import * as ifThenElse from './examples/if_then_else';
+import * as jsonschema from './examples/jsonschema';
+import * as jsoneditor from './examples/json-editor';
export {
issue_1948,
@@ -122,6 +126,8 @@ export {
oneOfRecursive,
huge,
ifThenElse,
+ jsoneditor,
+ jsonschema,
onChange,
enumExample,
radioGroupExample,
@@ -134,6 +140,8 @@ export {
additionalErrors,
additionalProperties,
login,
+ mixed,
+ mixedObject,
issue_1884,
arrayWithDefaults,
string,
diff --git a/packages/vue-vuetify/src/additional/ListWithDetailRenderer.vue b/packages/vue-vuetify/src/additional/ListWithDetailRenderer.vue
index 7d77ae9ee..1451da8be 100644
--- a/packages/vue-vuetify/src/additional/ListWithDetailRenderer.vue
+++ b/packages/vue-vuetify/src/additional/ListWithDetailRenderer.vue
@@ -29,9 +29,9 @@
:disabled="
!control.enabled ||
(appliedOptions.restrict &&
- arraySchema !== undefined &&
- arraySchema.maxItems !== undefined &&
- dataLength >= arraySchema.maxItems)
+ control.arraySchema !== undefined &&
+ control.arraySchema.maxItems !== undefined &&
+ dataLength >= control.arraySchema.maxItems)
"
>
{{ icons.current.value.itemAdd }}
@@ -150,9 +150,9 @@
:disabled="
!control.enabled ||
(appliedOptions.restrict &&
- arraySchema !== undefined &&
- arraySchema.minItems !== undefined &&
- dataLength <= arraySchema.minItems)
+ control.arraySchema !== undefined &&
+ control.arraySchema.minItems !== undefined &&
+ dataLength <= control.arraySchema.minItems)
"
>
{{
@@ -190,17 +190,10 @@
+
diff --git a/packages/vue-vuetify/src/complex/ObjectRenderer.entry.ts b/packages/vue-vuetify/src/complex/ObjectRenderer.entry.ts
index 0852ea8ef..66da2231c 100644
--- a/packages/vue-vuetify/src/complex/ObjectRenderer.entry.ts
+++ b/packages/vue-vuetify/src/complex/ObjectRenderer.entry.ts
@@ -2,10 +2,24 @@ import {
isObjectControl,
rankWith,
type JsonFormsRendererRegistryEntry,
+ type JsonSchema,
+ type Scopable,
+ type TesterContext,
+ type UISchemaElement,
} from '@jsonforms/core';
import controlRenderer from './ObjectRenderer.vue';
+import isEmpty from 'lodash/isEmpty';
export const entry: JsonFormsRendererRegistryEntry = {
renderer: controlRenderer,
- tester: rankWith(2, isObjectControl),
+ tester: rankWith(
+ 2,
+ (
+ uischema: UISchemaElement & Scopable,
+ schema: JsonSchema,
+ context: TesterContext,
+ ) =>
+ isObjectControl(uischema, schema, context) ||
+ (uischema.type === 'Object' && !isEmpty(uischema.scope)),
+ ),
};
diff --git a/packages/vue-vuetify/src/complex/ObjectRenderer.vue b/packages/vue-vuetify/src/complex/ObjectRenderer.vue
index ea767759f..36a38a063 100644
--- a/packages/vue-vuetify/src/complex/ObjectRenderer.vue
+++ b/packages/vue-vuetify/src/complex/ObjectRenderer.vue
@@ -10,13 +10,14 @@
:cells="control.cells"
/>
diff --git a/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue b/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue
index 565452923..9608262d7 100644
--- a/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue
+++ b/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue
@@ -65,7 +65,7 @@ import { defineComponent } from 'vue';
import { VAutocomplete, VSelect } from 'vuetify/components';
import { default as ControlWrapper } from '../controls/ControlWrapper.vue';
import { DisabledIconFocus } from '../controls/directives';
-import { useVuetifyControl } from '../util';
+import { determineClearValue, useVuetifyControl } from '../util';
const controlRenderer = defineComponent({
name: 'autocomplete-enum-control-renderer',
@@ -81,9 +81,10 @@ const controlRenderer = defineComponent({
...rendererProps(),
},
setup(props: RendererProps) {
+ const clearValue = determineClearValue('');
return useVuetifyControl(
useJsonFormsEnumControl(props),
- (value) => (value !== null ? value : undefined),
+ (value) => (value === null ? clearValue : value),
300,
);
},
diff --git a/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue b/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue
index e7eb31339..03e9ed970 100644
--- a/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue
+++ b/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue
@@ -65,7 +65,7 @@ import { defineComponent } from 'vue';
import { VAutocomplete, VSelect } from 'vuetify/components';
import { default as ControlWrapper } from '../controls/ControlWrapper.vue';
import { DisabledIconFocus } from '../controls/directives';
-import { useVuetifyControl } from '../util';
+import { determineClearValue, useVuetifyControl } from '../util';
const controlRenderer = defineComponent({
name: 'autocomplete-oneof-enum-control-renderer',
@@ -81,9 +81,10 @@ const controlRenderer = defineComponent({
...rendererProps(),
},
setup(props: RendererProps) {
+ const clearValue = determineClearValue('');
return useVuetifyControl(
useJsonFormsOneOfEnumControl(props),
- (value) => (value !== null ? value : undefined),
+ (value) => (value === null ? clearValue : value),
300,
);
},
diff --git a/packages/vue-vuetify/src/layouts/ArrayLayoutRenderer.vue b/packages/vue-vuetify/src/layouts/ArrayLayoutRenderer.vue
index f39a94e36..b0263a00c 100644
--- a/packages/vue-vuetify/src/layouts/ArrayLayoutRenderer.vue
+++ b/packages/vue-vuetify/src/layouts/ArrayLayoutRenderer.vue
@@ -70,11 +70,13 @@
>
-
-
+
+
@@ -265,13 +267,11 @@