Skip to content

Commit 6b216ca

Browse files
cernymatejbenjamincanac
andauthoredJul 22, 2024··
feat(SelectMenu): add selected to label / leading / trailing slots props (#1349)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>

File tree

1 file changed

+35
-13
lines changed

1 file changed

+35
-13
lines changed
 

‎src/runtime/components/forms/SelectMenu.vue

+35-13
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
v-slot="{ open }"
55
:by="by"
66
:name="name"
7-
:model-value="modelValue"
7+
:model-value="multiple ? (Array.isArray(modelValue) ? modelValue : []) : modelValue"
88
:multiple="multiple"
99
:disabled="disabled"
1010
as="div"
@@ -30,18 +30,18 @@
3030
<slot :open="open" :disabled="disabled" :loading="loading">
3131
<button :id="inputId" :class="selectClass" :disabled="disabled" type="button" v-bind="attrs">
3232
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
33-
<slot name="leading" :disabled="disabled" :loading="loading">
33+
<slot name="leading" :selected="selected" :disabled="disabled" :loading="loading">
3434
<UIcon :name="leadingIconName" :class="leadingIconClass" />
3535
</slot>
3636
</span>
3737

38-
<slot name="label">
38+
<slot name="label" :selected="selected">
3939
<span v-if="label" :class="uiMenu.label">{{ label }}</span>
4040
<span v-else :class="uiMenu.label">{{ placeholder || '&nbsp;' }}</span>
4141
</slot>
4242

4343
<span v-if="(isTrailing && trailingIconName) || $slots.trailing" :class="trailingWrapperIconClass">
44-
<slot name="trailing" :disabled="disabled" :loading="loading">
44+
<slot name="trailing" :selected="selected" :disabled="disabled" :loading="loading">
4545
<UIcon :name="trailingIconName" :class="trailingIconClass" aria-hidden="true" />
4646
</slot>
4747
</span>
@@ -68,15 +68,15 @@
6868
<component
6969
:is="searchable ? 'HComboboxOption' : 'HListboxOption'"
7070
v-for="(option, index) in filteredOptions"
71-
v-slot="{ active, selected, disabled: optionDisabled }"
71+
v-slot="{ active, selected: optionSelected, disabled: optionDisabled }"
7272
:key="index"
7373
as="template"
7474
:value="valueAttribute ? option[valueAttribute] : option"
7575
:disabled="option.disabled"
7676
>
77-
<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]">
77+
<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]">
7878
<div :class="uiMenu.option.container">
79-
<slot name="option" :option="option" :active="active" :selected="selected">
79+
<slot name="option" :option="option" :active="active" :selected="optionSelected">
8080
<UIcon v-if="option.icon" :name="option.icon" :class="[uiMenu.option.icon.base, active ? uiMenu.option.icon.active : uiMenu.option.icon.inactive, option.iconClass]" aria-hidden="true" />
8181
<UAvatar
8282
v-else-if="option.avatar"
@@ -90,16 +90,16 @@
9090
</slot>
9191
</div>
9292

93-
<span v-if="selected" :class="[uiMenu.option.selectedIcon.wrapper, uiMenu.option.selectedIcon.padding]">
93+
<span v-if="optionSelected" :class="[uiMenu.option.selectedIcon.wrapper, uiMenu.option.selectedIcon.padding]">
9494
<UIcon :name="selectedIcon" :class="uiMenu.option.selectedIcon.base" aria-hidden="true" />
9595
</span>
9696
</li>
9797
</component>
9898

99-
<component :is="searchable ? 'HComboboxOption' : 'HListboxOption'" v-if="creatable && createOption" v-slot="{ active, selected }" :value="createOption" as="template">
99+
<component :is="searchable ? 'HComboboxOption' : 'HListboxOption'" v-if="creatable && createOption" v-slot="{ active, selected: optionSelected }" :value="createOption" as="template">
100100
<li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive]">
101101
<div :class="uiMenu.option.container">
102-
<slot name="option-create" :option="createOption" :active="active" :selected="selected">
102+
<slot name="option-create" :option="createOption" :active="active" :selected="optionSelected">
103103
<span :class="uiMenu.option.create">Create "{{ createOption[optionAttribute] }}"</span>
104104
</slot>
105105
</div>
@@ -335,6 +335,10 @@ export default defineComponent({
335335
},
336336
emits: ['update:modelValue', 'update:query', 'open', 'close', 'change'],
337337
setup (props, { emit, slots }) {
338+
if (import.meta.dev && props.multiple && !Array.isArray(props.modelValue)) {
339+
console.warn(`[@nuxt/ui] The USelectMenu components needs to have a modelValue of type Array when using the multiple prop. Got '${typeof props.modelValue}' instead.`, props.modelValue)
340+
}
341+
338342
const { ui, attrs } = useUI('select', toRef(props, 'ui'), config, toRef(props, 'class'))
339343
const { ui: uiMenu } = useUI('selectMenu', toRef(props, 'uiMenu'), configMenu)
340344
@@ -358,17 +362,34 @@ export default defineComponent({
358362
}
359363
})
360364
365+
const selected = computed(() => {
366+
if (props.multiple) {
367+
if (!Array.isArray(props.modelValue) || !props.modelValue.length) {
368+
return []
369+
}
370+
371+
if (props.valueAttribute) {
372+
return options.value.filter(option => (props.modelValue as any[]).includes(option[props.valueAttribute]))
373+
}
374+
return options.value.filter(option => (props.modelValue as any[]).includes(option))
375+
}
376+
377+
if (props.valueAttribute) {
378+
return options.value.find(option => option[props.valueAttribute] === props.modelValue)
379+
}
380+
return options.value.find(option => option === props.modelValue)
381+
})
382+
361383
const label = computed(() => {
362384
if (props.multiple) {
363385
if (Array.isArray(props.modelValue) && props.modelValue.length) {
364-
return `${props.modelValue.length} selected`
386+
return `${selected.value.length} selected`
365387
} else {
366388
return null
367389
}
368390
} else if (props.modelValue !== undefined && props.modelValue !== null) {
369391
if (props.valueAttribute) {
370-
const option = options.value.find(option => option[props.valueAttribute] === props.modelValue)
371-
return option ? option[props.optionAttribute] : null
392+
return selected.value?.[props.optionAttribute] ?? null
372393
} else {
373394
return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : props.modelValue[props.optionAttribute]
374395
}
@@ -543,6 +564,7 @@ export default defineComponent({
543564
popper,
544565
trigger,
545566
container,
567+
selected,
546568
label,
547569
isLeading,
548570
isTrailing,

0 commit comments

Comments
 (0)
Please sign in to comment.