feat(SelectMenu): add clearble

This commit is contained in:
rdjanuar
2024-11-08 14:02:54 +07:00
parent 56e28d80db
commit 592da565fe
4 changed files with 73 additions and 26 deletions

View File

@@ -1,23 +1,21 @@
<template>
<UContainer class="min-h-screen flex items-center">
<UCard class="flex-1" :ui="{ background: 'bg-gray-50 dark:bg-gray-800/50', ring: 'ring-1 ring-gray-300 dark:ring-gray-700', divide: 'divide-y divide-gray-300 dark:divide-gray-700', header: { base: 'font-bold' } }">
<template #header>
Welcome to the playground!
</template>
<script setup lang="ts">
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
<p class="text-gray-500 dark:text-gray-400">
Try your components here!
</p>
</UCard>
</UContainer>
</template>
<script setup>
const selected = ref()
const handleClose = () => {
}
</script>
<style>
body {
@apply antialiased font-sans text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-900;
}
</style>
<template>
<UContainer>
<USelectMenu
v-model="selected"
multiple
clearable
:options="people"
placeholder="Select people"
@clear="handleClose"
/>
</UContainer>
</template>

View File

@@ -41,8 +41,11 @@
</slot>
<span v-if="(isTrailing && trailingIconName) || $slots.trailing" :class="trailingWrapperIconClass">
<slot name="trailing" :selected="selected" :disabled="disabled" :loading="loading">
<UIcon :name="trailingIconName" :class="trailingIconClass" aria-hidden="true" />
<slot
name="trailing"
v-bind="trailingSlotProps()"
>
<UIcon :name="trailingIconName" :class="trailingIconClass" aria-hidden="true" @click="onClear" />
</slot>
</span>
</button>
@@ -332,9 +335,21 @@ export default defineComponent({
uiMenu: {
type: Object as PropType<DeepPartial<typeof configMenu> & { strategy?: Strategy }>,
default: () => ({})
},
clearable: {
type: Boolean,
default: false
},
clearableIcon: {
type: String,
default: () => config.default.clerableIcon
},
closeOnClear: {
type: Boolean,
default: () => configMenu.default.closeOnClear
}
},
emits: ['update:modelValue', 'update:query', 'open', 'close', 'change'],
emits: ['update:modelValue', 'update:query', 'open', 'close', 'change', 'clear'],
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)
@@ -431,7 +446,12 @@ export default defineComponent({
return props.leadingIcon || props.icon
})
const canClearValue = computed(() => props.clearable && (Array.isArray(selected.value) ? selected.value.length > 0 : !!selected.value))
const trailingIconName = computed(() => {
if (canClearValue.value) {
return props.clearableIcon
}
if (props.loading && !isLeading.value) {
return props.loadingIcon
}
@@ -534,7 +554,7 @@ export default defineComponent({
return ['string', 'number'].includes(typeof props.modelValue) ? query.value : { [props.optionAttribute]: query.value }
})
function clearOnClose() {
function handleClearSearchOnClose() {
if (props.clearSearchOnClose) {
query.value = ''
}
@@ -544,7 +564,7 @@ export default defineComponent({
if (value) {
emit('open')
} else {
clearOnClose()
handleClearSearchOnClose()
emit('close')
emitFormBlur()
}
@@ -564,6 +584,31 @@ export default defineComponent({
query.value = event.target.value
}
function onClear(e: Event) {
if (canClearValue.value) {
if (!props.closeOnClear) {
e.stopPropagation()
}
emit('update:modelValue', props.multiple ? [] : null)
emit('clear')
emitFormChange()
}
}
function trailingSlotProps() {
const slotProps: Record<string, any> = {
selected: selected.value,
loading: props.loading,
disabled: props.disabled
}
if (props.clearable) {
slotProps.onClear = onClear
}
return slotProps
}
provideUseId(() => useId())
return {
@@ -583,6 +628,7 @@ export default defineComponent({
label,
accessor,
isLeading,
onClear,
isTrailing,
// eslint-disable-next-line vue/no-dupe-keys
selectClass,
@@ -597,7 +643,8 @@ export default defineComponent({
// eslint-disable-next-line vue/no-dupe-keys
query,
onUpdate,
onQueryChange
onQueryChange,
trailingSlotProps
}
}
})

View File

@@ -9,6 +9,7 @@ export default {
color: 'white',
variant: 'outline',
loadingIcon: 'i-heroicons-arrow-path-20-solid',
trailingIcon: 'i-heroicons-chevron-down-20-solid'
trailingIcon: 'i-heroicons-chevron-down-20-solid',
clerableIcon: 'i-heroicons-x-mark-20-solid'
}
}

View File

@@ -23,6 +23,7 @@ export default {
default: {
selectedIcon: 'i-heroicons-check-20-solid',
clearSearchOnClose: false,
closeOnClear: true,
showCreateOptionWhen: 'empty',
searchablePlaceholder: {
label: 'Search...'