From 1142db1c9059b386db14eabde44edc4660cbe919 Mon Sep 17 00:00:00 2001 From: Matej Cerny Date: Fri, 20 Sep 2024 17:31:28 +0200 Subject: [PATCH 1/6] fix(SelectMenu): use `by` prop to compare objects for selected values --- src/runtime/components/forms/SelectMenu.vue | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/runtime/components/forms/SelectMenu.vue b/src/runtime/components/forms/SelectMenu.vue index 34f23caf2d..3d7289f2c4 100644 --- a/src/runtime/components/forms/SelectMenu.vue +++ b/src/runtime/components/forms/SelectMenu.vue @@ -372,6 +372,15 @@ export default defineComponent({ if (props.valueAttribute) { return options.value.filter(option => (props.modelValue as any[]).includes(option[props.valueAttribute])) } + + if (props.by) { + return options.value.filter( + option => typeof option === 'object' && option !== null && props.modelValue.some( + (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.by] + ) + ) + } + return options.value.filter(option => (props.modelValue as any[]).includes(option)) } @@ -481,7 +490,7 @@ export default defineComponent({ } return props.options || [] - }, [], { + }, props.options || [], { lazy: props.searchableLazy }) From 119c3dfd8b9107724815460efaa74d1f18a6c844 Mon Sep 17 00:00:00 2001 From: Matej Cerny Date: Fri, 20 Sep 2024 17:47:45 +0200 Subject: [PATCH 2/6] fix(SelectMenu): add modelValue type --- src/runtime/components/forms/SelectMenu.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/components/forms/SelectMenu.vue b/src/runtime/components/forms/SelectMenu.vue index 3d7289f2c4..d41a462a79 100644 --- a/src/runtime/components/forms/SelectMenu.vue +++ b/src/runtime/components/forms/SelectMenu.vue @@ -375,7 +375,7 @@ export default defineComponent({ if (props.by) { return options.value.filter( - option => typeof option === 'object' && option !== null && props.modelValue.some( + option => typeof option === 'object' && option !== null && (props.modelValue as any[]).some( (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.by] ) ) From 647dbebf7775498289aa5337d812c43558c3042b Mon Sep 17 00:00:00 2001 From: Matej Cerny Date: Fri, 20 Sep 2024 18:07:10 +0200 Subject: [PATCH 3/6] feat(SelectMenu): handle nested `valueAttribute` comparison using the `by` prop --- src/runtime/components/forms/SelectMenu.vue | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/runtime/components/forms/SelectMenu.vue b/src/runtime/components/forms/SelectMenu.vue index d41a462a79..0ebef663fe 100644 --- a/src/runtime/components/forms/SelectMenu.vue +++ b/src/runtime/components/forms/SelectMenu.vue @@ -365,23 +365,35 @@ export default defineComponent({ const selected = computed(() => { if (props.multiple) { - if (!Array.isArray(props.modelValue) || !props.modelValue.length) { + const modelValue = props.modelValue + if (!Array.isArray(modelValue) || !modelValue.length) { return [] } if (props.valueAttribute) { - return options.value.filter(option => (props.modelValue as any[]).includes(option[props.valueAttribute])) + if (props.by) { + // handle the case when the valueAttribute is an object, and we need to compare by a specific key + return options.value.filter( + option => typeof option === 'object' && option !== null + && typeof option[props.valueAttribute] === 'object' && option[props.valueAttribute] !== null && modelValue.some( + (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.valueAttribute][props.by] + ) + ) + } + + // compute selected items by the valueAttribute form the options + return options.value.filter(option => modelValue.includes(option[props.valueAttribute])) } if (props.by) { return options.value.filter( - option => typeof option === 'object' && option !== null && (props.modelValue as any[]).some( + option => typeof option === 'object' && option !== null && modelValue.some( (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.by] ) ) } - return options.value.filter(option => (props.modelValue as any[]).includes(option)) + return options.value.filter(option => modelValue.includes(option)) } if (props.valueAttribute) { From 6694fae2db4bcb1c655a522823ad894082256ff8 Mon Sep 17 00:00:00 2001 From: Matej Cerny Date: Wed, 6 Nov 2024 08:08:49 +0100 Subject: [PATCH 4/6] style(select-menu): fix indentation --- src/runtime/components/forms/SelectMenu.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/components/forms/SelectMenu.vue b/src/runtime/components/forms/SelectMenu.vue index 0ebef663fe..d458963031 100644 --- a/src/runtime/components/forms/SelectMenu.vue +++ b/src/runtime/components/forms/SelectMenu.vue @@ -376,8 +376,8 @@ export default defineComponent({ return options.value.filter( option => typeof option === 'object' && option !== null && typeof option[props.valueAttribute] === 'object' && option[props.valueAttribute] !== null && modelValue.some( - (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.valueAttribute][props.by] - ) + (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.valueAttribute][props.by] + ) ) } From 70866d132bc8c5898b82eed82f0602ec43c62f62 Mon Sep 17 00:00:00 2001 From: Matej Cerny Date: Thu, 7 Nov 2024 01:17:10 +0100 Subject: [PATCH 5/6] fix(select-menu): add support for dot notation in value-attribute --- src/runtime/components/forms/SelectMenu.vue | 66 +++++++++++++++------ 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/src/runtime/components/forms/SelectMenu.vue b/src/runtime/components/forms/SelectMenu.vue index 9566324c61..73750256f2 100644 --- a/src/runtime/components/forms/SelectMenu.vue +++ b/src/runtime/components/forms/SelectMenu.vue @@ -71,7 +71,7 @@ v-slot="{ active, selected: optionSelected, disabled: optionDisabled }" :key="index" as="template" - :value="valueAttribute ? option[valueAttribute] : option" + :value="valueAttribute ? accessor(option, valueAttribute) : option" :disabled="option.disabled" >
  • @@ -364,6 +364,9 @@ export default defineComponent({ }) const selected = computed(() => { + const options = props.options || [] + + // handle multiple modelValue options if (props.multiple) { const modelValue = props.modelValue if (!Array.isArray(modelValue) || !modelValue.length) { @@ -372,34 +375,61 @@ export default defineComponent({ if (props.valueAttribute) { if (props.by) { - // handle the case when the valueAttribute is an object, and we need to compare by a specific key - return options.value.filter( - option => typeof option === 'object' && option !== null - && typeof option[props.valueAttribute] === 'object' && option[props.valueAttribute] !== null && modelValue.some( - (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.valueAttribute][props.by] - ) + // array of objects compared based on the attribute provided by the `by` prop + return options.filter( + (_option) => { + const option = typeof _option === 'object' && _option !== null ? accessor(_option, props.valueAttribute) : null + return typeof option === 'object' && option !== null && modelValue.some( + (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.by] + ) + } ) } - // compute selected items by the valueAttribute form the options - return options.value.filter(option => modelValue.includes(option[props.valueAttribute])) + // array of items compared based on their value (or reference, in case of objects) + return options.filter(option => + modelValue.includes(typeof option === 'object' && option !== null ? accessor(option, props.valueAttribute) : option) + ) } if (props.by) { - return options.value.filter( + return options.filter( option => typeof option === 'object' && option !== null && modelValue.some( (value: any) => typeof value === 'object' && value !== null && value[props.by] === option[props.by] ) ) } - return options.value.filter(option => modelValue.includes(option)) - } + return options.filter(option => modelValue.includes(option)) + } // end of multiple modelValue options handling + + // handle single modelValue option of an `object` type + if (typeof props.modelValue === 'object' && props.modelValue !== null) { + if (props.valueAttribute) { + if (props.by) { + return options.find( + (_option) => { + const option = typeof _option === 'object' && _option !== null ? accessor(_option, props.valueAttribute) : null + return typeof option === 'object' && option !== null && option[props.by] === props.modelValue[props.by] + } + ) + } - if (props.valueAttribute) { - return options.value.find(option => option[props.valueAttribute] === props.modelValue) + return options.find(option => (typeof option === 'object' && option !== null ? accessor(option, props.valueAttribute) : option) === toRaw(props.modelValue)) + } + + if (props.by) { + return options.find(option => (typeof option === 'object' && option !== null ? option[props.by] : option) === props.modelValue[props.by]) + } } - return options.value.find(option => option === props.modelValue) + + // handle single modelValue option of a primitive type + return options.find(option => + (typeof option === 'object' && option !== null && props.valueAttribute + ? accessor(option, props.valueAttribute) + : option + ) === toRaw(props.modelValue) + ) }) const label = computed(() => { @@ -410,10 +440,10 @@ export default defineComponent({ return null } } else if (props.modelValue !== undefined && props.modelValue !== null) { - if (props.valueAttribute) { - return accessor(selected.value, props.optionAttribute) ?? null + if (props.optionAttribute) { + return typeof selected.value === 'object' && selected.value !== null ? accessor(selected.value, props.optionAttribute) ?? null : selected.value ?? null } else { - return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : accessor(props.modelValue as Record, props.optionAttribute) + return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : accessor(toRaw(props.modelValue) as Record, props.optionAttribute) } } From b6a65e711090e66375149fa71897d68c0b256a6c Mon Sep 17 00:00:00 2001 From: Matej Cerny Date: Thu, 7 Nov 2024 01:43:59 +0100 Subject: [PATCH 6/6] fix(select-menu): update label handling --- src/runtime/components/forms/SelectMenu.vue | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/runtime/components/forms/SelectMenu.vue b/src/runtime/components/forms/SelectMenu.vue index 73750256f2..1a6ee066f2 100644 --- a/src/runtime/components/forms/SelectMenu.vue +++ b/src/runtime/components/forms/SelectMenu.vue @@ -440,11 +440,9 @@ export default defineComponent({ return null } } else if (props.modelValue !== undefined && props.modelValue !== null) { - if (props.optionAttribute) { - return typeof selected.value === 'object' && selected.value !== null ? accessor(selected.value, props.optionAttribute) ?? null : selected.value ?? null - } else { - return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : accessor(toRaw(props.modelValue) as Record, props.optionAttribute) - } + return typeof selected.value === 'object' && selected.value !== null && props.optionAttribute + ? accessor(selected.value, props.optionAttribute) ?? null + : selected.value } return null