From a059092cf78b1187ff9ec1a9f6f1411f1255ab47 Mon Sep 17 00:00:00 2001 From: Krasimir Chobantonov Date: Thu, 21 Aug 2025 11:07:17 -0400 Subject: [PATCH 1/7] fix how we handle empty values when not in dynamic context (e.g. additionalProperties case) --- .../src/controls/AnyOfStringOrEnumControlRenderer.vue | 5 +++-- packages/vue-vuetify/src/controls/DateControlRenderer.vue | 2 +- .../vue-vuetify/src/controls/DateTimeControlRenderer.vue | 2 +- packages/vue-vuetify/src/controls/EnumControlRenderer.vue | 2 +- .../vue-vuetify/src/controls/MultiStringControlRenderer.vue | 2 +- packages/vue-vuetify/src/controls/StringControlRenderer.vue | 2 +- .../vue-vuetify/src/controls/StringMaskControlRenderer.vue | 2 +- packages/vue-vuetify/src/controls/TimeControlRenderer.vue | 2 +- .../src/extended/AutocompleteEnumControlRenderer.vue | 2 +- .../src/extended/AutocompleteOneOfEnumControlRenderer.vue | 2 +- 10 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/vue-vuetify/src/controls/AnyOfStringOrEnumControlRenderer.vue b/packages/vue-vuetify/src/controls/AnyOfStringOrEnumControlRenderer.vue index ef5a349e40..eb45c8400d 100644 --- a/packages/vue-vuetify/src/controls/AnyOfStringOrEnumControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/AnyOfStringOrEnumControlRenderer.vue @@ -63,8 +63,9 @@ const controlRenderer = defineComponent({ }, setup(props: RendererProps) { const clearValue = determineClearValue(''); - return useVuetifyControl(useJsonFormsControl(props), (value) => - value === null ? clearValue : value, + return useVuetifyControl( + useJsonFormsControl(props), + (value) => value || clearValue, ); }, computed: { diff --git a/packages/vue-vuetify/src/controls/DateControlRenderer.vue b/packages/vue-vuetify/src/controls/DateControlRenderer.vue index 0090aa2f8c..af5613b87d 100644 --- a/packages/vue-vuetify/src/controls/DateControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/DateControlRenderer.vue @@ -141,7 +141,7 @@ const controlRenderer = defineComponent({ const showMenu = ref(false); - const adaptValue = (value: any) => (value === null ? clearValue : value); + const adaptValue = (value: any) => value || clearValue; const control = useVuetifyControl(useJsonFormsControl(props), adaptValue); const dateFormat = computed( diff --git a/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue b/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue index fd6e01c113..c80f0d342c 100644 --- a/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue @@ -289,7 +289,7 @@ const controlRenderer = defineComponent({ const t = useTranslator(); const showMenu = ref(false); const activeTab = ref<'date' | 'time'>('date'); - const adaptValue = (value: any) => (value === null ? clearValue : value); + const adaptValue = (value: any) => value || clearValue; const control = useVuetifyControl(useJsonFormsControl(props), adaptValue); const { mobile } = useDisplay(); diff --git a/packages/vue-vuetify/src/controls/EnumControlRenderer.vue b/packages/vue-vuetify/src/controls/EnumControlRenderer.vue index 709e9b4feb..c480394c71 100644 --- a/packages/vue-vuetify/src/controls/EnumControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/EnumControlRenderer.vue @@ -58,7 +58,7 @@ const controlRenderer = defineComponent({ setup(props: RendererProps) { const clearValue = determineClearValue(''); return useVuetifyControl(useJsonFormsEnumControl(props), (value) => - value === null ? clearValue : value, + value !== null ? value : clearValue, ); }, }); diff --git a/packages/vue-vuetify/src/controls/MultiStringControlRenderer.vue b/packages/vue-vuetify/src/controls/MultiStringControlRenderer.vue index 4a01d84e78..3616cb59dc 100644 --- a/packages/vue-vuetify/src/controls/MultiStringControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/MultiStringControlRenderer.vue @@ -65,7 +65,7 @@ const controlRenderer = defineComponent({ const clearValue = determineClearValue(''); return useVuetifyControl( useJsonFormsControl(props), - (value) => (value === null ? clearValue : value), + (value) => value || clearValue, 300, ); }, diff --git a/packages/vue-vuetify/src/controls/StringControlRenderer.vue b/packages/vue-vuetify/src/controls/StringControlRenderer.vue index 0a3f94a83a..d2b692fd00 100644 --- a/packages/vue-vuetify/src/controls/StringControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/StringControlRenderer.vue @@ -99,7 +99,7 @@ const controlRenderer = defineComponent({ const clearValue = determineClearValue(''); return useVuetifyControl( useJsonFormsControl(props), - (value) => (value === null ? clearValue : value), + (value) => value || clearValue, 300, ); }, diff --git a/packages/vue-vuetify/src/controls/StringMaskControlRenderer.vue b/packages/vue-vuetify/src/controls/StringMaskControlRenderer.vue index 7cd870fac5..90317f6f88 100644 --- a/packages/vue-vuetify/src/controls/StringMaskControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/StringMaskControlRenderer.vue @@ -77,7 +77,7 @@ const controlRenderer = defineComponent({ }, setup(props: RendererProps) { const clearValue = determineClearValue(''); - const adaptValue = (value: any) => (value === null ? clearValue : value); + const adaptValue = (value: any) => value || clearValue; const control = useVuetifyControl(useJsonFormsControl(props), adaptValue); const toTokens = (tokenParams: Record): MaskTokens => { diff --git a/packages/vue-vuetify/src/controls/TimeControlRenderer.vue b/packages/vue-vuetify/src/controls/TimeControlRenderer.vue index 3cfc4181e0..9dc55993c3 100644 --- a/packages/vue-vuetify/src/controls/TimeControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/TimeControlRenderer.vue @@ -163,7 +163,7 @@ const controlRenderer = defineComponent({ const showMenu = ref(false); - const adaptValue = (value: any) => (value === null ? clearValue : value); + const adaptValue = (value: any) => value || clearValue; const control = useVuetifyControl(useJsonFormsControl(props), adaptValue); const icons = useIcons(); diff --git a/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue b/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue index 9608262d73..617d3aba03 100644 --- a/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue +++ b/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue @@ -84,7 +84,7 @@ const controlRenderer = defineComponent({ const clearValue = determineClearValue(''); return useVuetifyControl( useJsonFormsEnumControl(props), - (value) => (value === null ? clearValue : value), + (value) => (value !== null ? value : clearValue), 300, ); }, diff --git a/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue b/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue index 03e9ed970a..f7d61709c5 100644 --- a/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue +++ b/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue @@ -84,7 +84,7 @@ const controlRenderer = defineComponent({ const clearValue = determineClearValue(''); return useVuetifyControl( useJsonFormsOneOfEnumControl(props), - (value) => (value === null ? clearValue : value), + (value) => (value !== null ? value : clearValue), 300, ); }, From cc45d6ad7f608ee0fb172406258ea23781080eef Mon Sep 17 00:00:00 2001 From: Krasimir Chobantonov Date: Fri, 22 Aug 2025 13:36:15 -0400 Subject: [PATCH 2/7] allow defaulting the schema --- packages/vue-vuetify/dev/views/ExampleView.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/vue-vuetify/dev/views/ExampleView.vue b/packages/vue-vuetify/dev/views/ExampleView.vue index e4274ab3d1..d995454f5d 100644 --- a/packages/vue-vuetify/dev/views/ExampleView.vue +++ b/packages/vue-vuetify/dev/views/ExampleView.vue @@ -114,7 +114,8 @@ const reloadMonacoSchema = () => { const saveMonacoSchema = () => { saveMonacoModel( schemaModel, - (modelValue) => (state.schema = JSON.parse(modelValue)), + (modelValue) => + (state.schema = modelValue ? JSON.parse(modelValue) : undefined), 'New schema applied', ); @@ -277,6 +278,10 @@ const handleAction = (action: Action) => { if (action) { const newState = action.apply(state); if (newState) { + if (newState.renderers) { + newState.renderers = markRaw(newState.renderers); + } + Object.assign(state, newState); } } From a8b7e7e9a5cca63bbb9002b937c1f9d8d304f44f Mon Sep 17 00:00:00 2001 From: Krasimir Chobantonov Date: Tue, 23 Sep 2025 18:53:02 -0400 Subject: [PATCH 3/7] update the determineClearValue --- packages/vue-vuetify/src/util/composition.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/vue-vuetify/src/util/composition.ts b/packages/vue-vuetify/src/util/composition.ts index cd3b095bc9..876c8bf315 100644 --- a/packages/vue-vuetify/src/util/composition.ts +++ b/packages/vue-vuetify/src/util/composition.ts @@ -484,12 +484,7 @@ export const useIcons = () => { }; export const determineClearValue = (defaultValue: any) => { - const jsonforms = useJsonForms(); - - const useDefaultValue = inject( - IsDynamicPropertyContext, - jsonforms.core?.schema.type !== 'object', - ); + const useDefaultValue = inject(IsDynamicPropertyContext, false); // undefined will clear the property from the object return useDefaultValue ? defaultValue : undefined; From a8cb7b2f2022cec69d842639cd515df094f35848 Mon Sep 17 00:00:00 2001 From: Krasimir Chobantonov Date: Wed, 24 Sep 2025 13:33:36 -0400 Subject: [PATCH 4/7] remove unnecessary type --- packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue b/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue index c80f0d342c..fe2cda34f7 100644 --- a/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue @@ -227,7 +227,7 @@ import { VWindowItem, } from 'vuetify/components'; -import { vMaska, type MaskOptions, type MaskaDetail } from 'maska'; +import { vMaska, type MaskOptions } from 'maska'; import { useDisplay, useLocale } from 'vuetify'; import type { IconValue } from '../icons'; import { From def55be69f3d9cb19bc7de263dce621444f1eb6c Mon Sep 17 00:00:00 2001 From: Krasimir Chobantonov Date: Wed, 24 Sep 2025 13:34:01 -0400 Subject: [PATCH 5/7] revert to the original expression --- packages/vue-vuetify/src/controls/EnumControlRenderer.vue | 2 +- .../src/extended/AutocompleteEnumControlRenderer.vue | 2 +- .../src/extended/AutocompleteOneOfEnumControlRenderer.vue | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vue-vuetify/src/controls/EnumControlRenderer.vue b/packages/vue-vuetify/src/controls/EnumControlRenderer.vue index c480394c71..709e9b4feb 100644 --- a/packages/vue-vuetify/src/controls/EnumControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/EnumControlRenderer.vue @@ -58,7 +58,7 @@ const controlRenderer = defineComponent({ setup(props: RendererProps) { const clearValue = determineClearValue(''); return useVuetifyControl(useJsonFormsEnumControl(props), (value) => - value !== null ? value : clearValue, + value === null ? clearValue : value, ); }, }); diff --git a/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue b/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue index 617d3aba03..9608262d73 100644 --- a/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue +++ b/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue @@ -84,7 +84,7 @@ const controlRenderer = defineComponent({ const clearValue = determineClearValue(''); return useVuetifyControl( useJsonFormsEnumControl(props), - (value) => (value !== null ? value : clearValue), + (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 f7d61709c5..03e9ed970a 100644 --- a/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue +++ b/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue @@ -84,7 +84,7 @@ const controlRenderer = defineComponent({ const clearValue = determineClearValue(''); return useVuetifyControl( useJsonFormsOneOfEnumControl(props), - (value) => (value !== null ? value : clearValue), + (value) => (value === null ? clearValue : value), 300, ); }, From 7022bd80071e7d99575c5751fdf47bd57b4b987a Mon Sep 17 00:00:00 2001 From: Krasimir Chobantonov Date: Sat, 27 Sep 2025 13:02:29 -0400 Subject: [PATCH 6/7] use :return-object="true" --- .../vue-vuetify/src/controls/EnumControlRenderer.vue | 5 +++-- .../src/controls/OneOfEnumControlRenderer.vue | 9 ++++++--- .../src/extended/AutocompleteEnumControlRenderer.vue | 4 +++- .../extended/AutocompleteOneOfEnumControlRenderer.vue | 4 +++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/vue-vuetify/src/controls/EnumControlRenderer.vue b/packages/vue-vuetify/src/controls/EnumControlRenderer.vue index 709e9b4feb..d40c3abf24 100644 --- a/packages/vue-vuetify/src/controls/EnumControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/EnumControlRenderer.vue @@ -22,6 +22,7 @@ :items="control.options" item-title="label" item-value="value" + :return-object="true" v-bind="vuetifyProps('v-select')" @update:model-value="onChange" @focus="handleFocus" @@ -57,8 +58,8 @@ const controlRenderer = defineComponent({ }, setup(props: RendererProps) { const clearValue = determineClearValue(''); - return useVuetifyControl(useJsonFormsEnumControl(props), (value) => - value === null ? clearValue : value, + return useVuetifyControl(useJsonFormsEnumControl(props), (item) => + item === null ? clearValue : item.value, ); }, }); diff --git a/packages/vue-vuetify/src/controls/OneOfEnumControlRenderer.vue b/packages/vue-vuetify/src/controls/OneOfEnumControlRenderer.vue index c5c48035b6..f136436060 100644 --- a/packages/vue-vuetify/src/controls/OneOfEnumControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/OneOfEnumControlRenderer.vue @@ -22,6 +22,7 @@ :items="control.options" item-title="label" item-value="value" + :return-object="true" v-bind="vuetifyProps('v-select')" @update:model-value="onChange" @focus="handleFocus" @@ -39,7 +40,7 @@ import { } from '@jsonforms/vue'; import { defineComponent } from 'vue'; import { VSelect } from 'vuetify/components'; -import { useVuetifyControl } from '../util'; +import { determineClearValue, useVuetifyControl } from '../util'; import { default as ControlWrapper } from './ControlWrapper.vue'; import { DisabledIconFocus } from './directives'; @@ -56,8 +57,10 @@ const controlRenderer = defineComponent({ ...rendererProps(), }, setup(props: RendererProps) { - return useVuetifyControl(useJsonFormsOneOfEnumControl(props), (value) => - value !== null ? value : undefined, + const clearValue = determineClearValue(''); + + return useVuetifyControl(useJsonFormsOneOfEnumControl(props), (item) => + item === null ? clearValue : item.value, ); }, }); diff --git a/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue b/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue index 9608262d73..5ebdff9eaf 100644 --- a/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue +++ b/packages/vue-vuetify/src/extended/AutocompleteEnumControlRenderer.vue @@ -23,6 +23,7 @@ :items="control.options" item-title="label" item-value="value" + :return-object="true" v-bind="vuetifyProps('v-select')" @update:model-value="onChange" @focus="handleFocus" @@ -46,6 +47,7 @@ :items="control.options" item-title="label" item-value="value" + :return-object="true" v-bind="vuetifyProps('v-autocomplete')" @update:model-value="onChange" @focus="handleFocus" @@ -84,7 +86,7 @@ const controlRenderer = defineComponent({ const clearValue = determineClearValue(''); return useVuetifyControl( useJsonFormsEnumControl(props), - (value) => (value === null ? clearValue : value), + (item) => (item === null ? clearValue : item.value), 300, ); }, diff --git a/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue b/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue index 03e9ed970a..2605814888 100644 --- a/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue +++ b/packages/vue-vuetify/src/extended/AutocompleteOneOfEnumControlRenderer.vue @@ -23,6 +23,7 @@ :items="control.options" item-title="label" item-value="value" + :return-object="true" v-bind="vuetifyProps('v-select')" @update:model-value="onChange" @focus="handleFocus" @@ -46,6 +47,7 @@ :items="control.options" item-title="label" item-value="value" + :return-object="true" v-bind="vuetifyProps('v-autocomplete')" @update:model-value="onChange" @focus="handleFocus" @@ -84,7 +86,7 @@ const controlRenderer = defineComponent({ const clearValue = determineClearValue(''); return useVuetifyControl( useJsonFormsOneOfEnumControl(props), - (value) => (value === null ? clearValue : value), + (item) => (item === null ? clearValue : item.value), 300, ); }, From ba454aa3f5400cc2f3fa116b46799f4649712315 Mon Sep 17 00:00:00 2001 From: Krasimir Chobantonov Date: Tue, 7 Oct 2025 12:09:59 -0400 Subject: [PATCH 7/7] use the same default format as react --- packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue b/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue index fe2cda34f7..8cf5be9a88 100644 --- a/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue +++ b/packages/vue-vuetify/src/controls/DateTimeControlRenderer.vue @@ -299,7 +299,7 @@ const controlRenderer = defineComponent({ typeof control.appliedOptions.value.dateTimeFormat == 'string' ? (expandLocaleFormat(control.appliedOptions.value.dateTimeFormat) ?? control.appliedOptions.value.dateTimeFormat) - : (expandLocaleFormat('L LT') ?? 'YYYY-MM-DD H:mm'), + : (expandLocaleFormat('L LT') ?? 'YYYY-MM-DD HH:mm'), ); const useMask = control.appliedOptions.value.mask !== false;