mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 20:19:34 +01:00
feat(SelectMenu): add selected to label / leading / trailing slots props (#1349)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
v-slot="{ open }"
|
||||
:by="by"
|
||||
:name="name"
|
||||
:model-value="modelValue"
|
||||
:model-value="multiple ? (Array.isArray(modelValue) ? modelValue : []) : modelValue"
|
||||
:multiple="multiple"
|
||||
:disabled="disabled"
|
||||
as="div"
|
||||
@@ -30,18 +30,18 @@
|
||||
<slot :open="open" :disabled="disabled" :loading="loading">
|
||||
<button :id="inputId" :class="selectClass" :disabled="disabled" type="button" v-bind="attrs">
|
||||
<span v-if="(isLeading && leadingIconName) || $slots.leading" :class="leadingWrapperIconClass">
|
||||
<slot name="leading" :disabled="disabled" :loading="loading">
|
||||
<slot name="leading" :selected="selected" :disabled="disabled" :loading="loading">
|
||||
<UIcon :name="leadingIconName" :class="leadingIconClass" />
|
||||
</slot>
|
||||
</span>
|
||||
|
||||
<slot name="label">
|
||||
<slot name="label" :selected="selected">
|
||||
<span v-if="label" :class="uiMenu.label">{{ label }}</span>
|
||||
<span v-else :class="uiMenu.label">{{ placeholder || ' ' }}</span>
|
||||
</slot>
|
||||
|
||||
<span v-if="(isTrailing && trailingIconName) || $slots.trailing" :class="trailingWrapperIconClass">
|
||||
<slot name="trailing" :disabled="disabled" :loading="loading">
|
||||
<slot name="trailing" :selected="selected" :disabled="disabled" :loading="loading">
|
||||
<UIcon :name="trailingIconName" :class="trailingIconClass" aria-hidden="true" />
|
||||
</slot>
|
||||
</span>
|
||||
@@ -68,15 +68,15 @@
|
||||
<component
|
||||
:is="searchable ? 'HComboboxOption' : 'HListboxOption'"
|
||||
v-for="(option, index) in filteredOptions"
|
||||
v-slot="{ active, selected, disabled: optionDisabled }"
|
||||
v-slot="{ active, selected: optionSelected, disabled: optionDisabled }"
|
||||
:key="index"
|
||||
as="template"
|
||||
:value="valueAttribute ? 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]">
|
||||
<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]">
|
||||
<div :class="uiMenu.option.container">
|
||||
<slot name="option" :option="option" :active="active" :selected="selected">
|
||||
<slot name="option" :option="option" :active="active" :selected="optionSelected">
|
||||
<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" />
|
||||
<UAvatar
|
||||
v-else-if="option.avatar"
|
||||
@@ -90,16 +90,16 @@
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
<span v-if="selected" :class="[uiMenu.option.selectedIcon.wrapper, uiMenu.option.selectedIcon.padding]">
|
||||
<span v-if="optionSelected" :class="[uiMenu.option.selectedIcon.wrapper, uiMenu.option.selectedIcon.padding]">
|
||||
<UIcon :name="selectedIcon" :class="uiMenu.option.selectedIcon.base" aria-hidden="true" />
|
||||
</span>
|
||||
</li>
|
||||
</component>
|
||||
|
||||
<component :is="searchable ? 'HComboboxOption' : 'HListboxOption'" v-if="creatable && createOption" v-slot="{ active, selected }" :value="createOption" as="template">
|
||||
<component :is="searchable ? 'HComboboxOption' : 'HListboxOption'" v-if="creatable && createOption" v-slot="{ active, selected: optionSelected }" :value="createOption" as="template">
|
||||
<li :class="[uiMenu.option.base, uiMenu.option.rounded, uiMenu.option.padding, uiMenu.option.size, uiMenu.option.color, active ? uiMenu.option.active : uiMenu.option.inactive]">
|
||||
<div :class="uiMenu.option.container">
|
||||
<slot name="option-create" :option="createOption" :active="active" :selected="selected">
|
||||
<slot name="option-create" :option="createOption" :active="active" :selected="optionSelected">
|
||||
<span :class="uiMenu.option.create">Create "{{ createOption[optionAttribute] }}"</span>
|
||||
</slot>
|
||||
</div>
|
||||
@@ -335,6 +335,10 @@ export default defineComponent({
|
||||
},
|
||||
emits: ['update:modelValue', 'update:query', 'open', 'close', 'change'],
|
||||
setup (props, { emit, slots }) {
|
||||
if (import.meta.dev && props.multiple && !Array.isArray(props.modelValue)) {
|
||||
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)
|
||||
}
|
||||
|
||||
const { ui, attrs } = useUI('select', toRef(props, 'ui'), config, toRef(props, 'class'))
|
||||
const { ui: uiMenu } = useUI('selectMenu', toRef(props, 'uiMenu'), configMenu)
|
||||
|
||||
@@ -358,17 +362,34 @@ export default defineComponent({
|
||||
}
|
||||
})
|
||||
|
||||
const selected = computed(() => {
|
||||
if (props.multiple) {
|
||||
if (!Array.isArray(props.modelValue) || !props.modelValue.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
if (props.valueAttribute) {
|
||||
return options.value.filter(option => (props.modelValue as any[]).includes(option[props.valueAttribute]))
|
||||
}
|
||||
return options.value.filter(option => (props.modelValue as any[]).includes(option))
|
||||
}
|
||||
|
||||
if (props.valueAttribute) {
|
||||
return options.value.find(option => option[props.valueAttribute] === props.modelValue)
|
||||
}
|
||||
return options.value.find(option => option === props.modelValue)
|
||||
})
|
||||
|
||||
const label = computed(() => {
|
||||
if (props.multiple) {
|
||||
if (Array.isArray(props.modelValue) && props.modelValue.length) {
|
||||
return `${props.modelValue.length} selected`
|
||||
return `${selected.value.length} selected`
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
} else if (props.modelValue !== undefined && props.modelValue !== null) {
|
||||
if (props.valueAttribute) {
|
||||
const option = options.value.find(option => option[props.valueAttribute] === props.modelValue)
|
||||
return option ? option[props.optionAttribute] : null
|
||||
return selected.value?.[props.optionAttribute] ?? null
|
||||
} else {
|
||||
return ['string', 'number'].includes(typeof props.modelValue) ? props.modelValue : props.modelValue[props.optionAttribute]
|
||||
}
|
||||
@@ -543,6 +564,7 @@ export default defineComponent({
|
||||
popper,
|
||||
trigger,
|
||||
container,
|
||||
selected,
|
||||
label,
|
||||
isLeading,
|
||||
isTrailing,
|
||||
|
||||
Reference in New Issue
Block a user