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