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 @@