Skip to content

Commit

Permalink
fix(InputMenu/SelectMenu): use by prop to compare objects & support…
Browse files Browse the repository at this point in the history
… dot notation in `value-attribute` (#2566)
  • Loading branch information
rdjanuar authored Nov 10, 2024
1 parent 49f85d5 commit 7154254
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 24 deletions.
25 changes: 23 additions & 2 deletions src/runtime/components/forms/InputMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
v-slot="{ active, selected, disabled: optionDisabled }"
:key="index"
as="template"
:value="valueAttribute ? option[valueAttribute] : option"
:value="valueAttribute ? accessor(option, valueAttribute) : option"
:disabled="option.disabled"
>
<li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive, selected && uiMenu.option.selected, optionDisabled && uiMenu.option.disabled]">
Expand Down Expand Up @@ -104,6 +104,7 @@ import {
import { computedAsync, useDebounceFn } from '@vueuse/core'
import { defu } from 'defu'
import { twMerge, twJoin } from 'tailwind-merge'
import { isEqual } from 'ohash'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import { useUI } from '../../composables/useUI'
Expand Down Expand Up @@ -308,8 +309,28 @@ export default defineComponent({
return
}
function getValue(value: any) {
if (props.valueAttribute) {
return accessor(value, props.valueAttribute)
}
return value
}
function compareValues(value1: any, value2: any) {
if (props.by && typeof value1 === 'object' && typeof value2 === 'object') {
return isEqual(value1[props.by], value2[props.by])
}
return isEqual(value1, value2)
}
if (props.valueAttribute) {
const option = options.value.find(option => option[props.valueAttribute] === props.modelValue)
const option = options.value.find((option) => {
const optionValue = getValue(option)
return compareValues(optionValue, props.modelValue)
})
return option ? accessor(option, props.optionAttribute) : null
} else {
return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : accessor(props.modelValue as Record<string, any>, props.optionAttribute)
Expand Down
59 changes: 37 additions & 22 deletions src/runtime/components/forms/SelectMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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"
>
<li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive, optionSelected && uiMenu.option.selected, optionDisabled && uiMenu.option.disabled]">
Expand Down Expand Up @@ -140,6 +140,7 @@ import {
import { computedAsync, useDebounceFn } from '@vueuse/core'
import { defu } from 'defu'
import { twMerge, twJoin } from 'tailwind-merge'
import { isEqual } from 'ohash'
import UIcon from '../elements/Icon.vue'
import UAvatar from '../elements/Avatar.vue'
import { useUI } from '../../composables/useUI'
Expand Down Expand Up @@ -364,39 +365,53 @@ export default defineComponent({
})
const selected = computed(() => {
if (props.multiple) {
if (!Array.isArray(props.modelValue) || !props.modelValue.length) {
return []
function compareValues(value1: any, value2: any) {
if (props.by && typeof value1 === 'object' && typeof value2 === 'object') {
return isEqual(value1[props.by], value2[props.by])
}
return isEqual(value1, value2)
}
function getValue(value: any) {
if (props.valueAttribute) {
return options.value.filter(option => (props.modelValue as any[]).includes(option[props.valueAttribute]))
return accessor(value, props.valueAttribute)
}
return options.value.filter(option => (props.modelValue as any[]).includes(option))
return value
}
if (props.valueAttribute) {
return options.value.find(option => option[props.valueAttribute] === props.modelValue)
if (props.multiple) {
const modelValue = props.modelValue
if (!Array.isArray(modelValue) || !modelValue.length) {
return []
}
return options.value.filter((option) => {
const optionValue = getValue(option)
return modelValue.some(value => compareValues(value, optionValue))
})
}
return options.value.find(option => option === props.modelValue)
return options.value.find((option) => {
const optionValue = getValue(option)
return compareValues(optionValue, toRaw(props.modelValue))
}) ?? props.modelValue
})
const label = computed(() => {
if (props.multiple) {
if (Array.isArray(props.modelValue) && props.modelValue.length) {
return `${selected.value.length} selected`
} else {
return null
}
} else if (props.modelValue !== undefined && props.modelValue !== null) {
if (props.valueAttribute) {
return accessor(selected.value, props.optionAttribute) ?? null
} else {
return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : accessor(props.modelValue as Record<string, any>, props.optionAttribute)
}
if (!selected.value) return null
if (props.valueAttribute) {
return accessor(selected.value as Record<string, any>, props.optionAttribute)
}
if (Array.isArray(props.modelValue) && props.modelValue.length) {
return `${props.modelValue.length} selected`
} else if (['string', 'number'].includes(typeof props.modelValue)) {
return props.modelValue
}
return null
return accessor(props.modelValue as Record<string, any>, props.optionAttribute)
})
const selectClass = computed(() => {
Expand Down

0 comments on commit 7154254

Please sign in to comment.