mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
@@ -38,7 +38,7 @@ const searchTermDebounced = refDebounced(searchTerm, 200)
|
||||
const { data: users, pending } = await useFetch('https://jsonplaceholder.typicode.com/users', {
|
||||
params: { q: searchTermDebounced },
|
||||
transform: (data: User[]) => {
|
||||
return data?.map(user => ({ label: user.name, value: user.id, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
return data?.map(user => ({ id: user.id, label: user.name, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
|
||||
},
|
||||
lazy: true
|
||||
})
|
||||
@@ -53,6 +53,7 @@ const { data: users, pending } = await useFetch('https://jsonplaceholder.typicod
|
||||
<USelectMenu :items="items" placeholder="Search..." variant="none" />
|
||||
<USelectMenu :items="items" placeholder="Disabled" disabled />
|
||||
<USelectMenu :items="items" placeholder="Required" required />
|
||||
<USelectMenu :items="items" placeholder="Multiple" multiple />
|
||||
<USelectMenu :items="items" loading placeholder="Search..." />
|
||||
<USelectMenu :items="items" loading leading-icon="i-heroicons-magnifying-glass" placeholder="Search..." />
|
||||
<USelectMenu :items="statuses" placeholder="Search status..." icon="i-heroicons-magnifying-glass" trailing-icon="i-heroicons-chevron-up-down-20-solid">
|
||||
@@ -67,6 +68,7 @@ const { data: users, pending } = await useFetch('https://jsonplaceholder.typicod
|
||||
:filter="false"
|
||||
icon="i-heroicons-user"
|
||||
placeholder="Search users..."
|
||||
@update:open="searchTerm = ''"
|
||||
>
|
||||
<template #leading="{ modelValue }">
|
||||
<UAvatar v-if="modelValue?.avatar" size="2xs" v-bind="modelValue.avatar" />
|
||||
|
||||
@@ -26,7 +26,7 @@ export interface SelectMenuItem extends Pick<ComboboxItemProps, 'disabled'> {
|
||||
|
||||
type SelectMenuVariants = VariantProps<typeof selectMenu>
|
||||
|
||||
export interface SelectMenuProps<T> extends Omit<ComboboxRootProps<T>, 'asChild' | 'dir' | 'filterFunction' | 'displayValue' | 'multiple'>, UseComponentIconsProps {
|
||||
export interface SelectMenuProps<T> extends Omit<ComboboxRootProps<T>, 'asChild' | 'dir' | 'filterFunction' | 'displayValue'>, UseComponentIconsProps {
|
||||
id?: string
|
||||
/** The placeholder text when the select is empty. */
|
||||
placeholder?: string
|
||||
@@ -97,7 +97,7 @@ const slots = defineSlots<SelectMenuSlots<T>>()
|
||||
const searchTerm = defineModel<string>('searchTerm', { default: '' })
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'open', 'defaultOpen'), emits)
|
||||
const rootProps = useForwardPropsEmits(reactivePick(props, 'as', 'modelValue', 'defaultValue', 'open', 'defaultOpen', 'multiple'), emits)
|
||||
const contentProps = toRef(() => defu(props.content, { side: 'bottom', sideOffset: 8, position: 'popper' }) as ComboboxContentProps)
|
||||
const { emitFormBlur, emitFormChange, size: formGroupSize, color, id, name, disabled } = useFormField<InputProps>(props)
|
||||
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
|
||||
@@ -115,7 +115,11 @@ const ui = computed(() => tv({ extend: selectMenu, slots: props.ui })({
|
||||
buttonGroup: orientation.value
|
||||
}))
|
||||
|
||||
function displayValue(val: AcceptableValue) {
|
||||
function displayValue(val: T, multiple?: boolean): string {
|
||||
if (multiple && Array.isArray(val)) {
|
||||
return val.map(v => displayValue(v)).join(', ')
|
||||
}
|
||||
|
||||
if (typeof val === 'object') {
|
||||
return val.label
|
||||
}
|
||||
@@ -155,7 +159,7 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
||||
as-child
|
||||
:name="name"
|
||||
:disabled="disabled"
|
||||
:display-value="displayValue"
|
||||
:display-value="() => searchTerm"
|
||||
:filter-function="filterFunction"
|
||||
@update:model-value="emitFormChange()"
|
||||
>
|
||||
@@ -168,8 +172,8 @@ const groups = computed(() => props.items?.length ? (Array.isArray(props.items[0
|
||||
</span>
|
||||
|
||||
<slot :model-value="(modelValue as T)" :open="open">
|
||||
<span v-if="displayValue(modelValue)" :class="ui.value()">
|
||||
{{ displayValue(modelValue) }}
|
||||
<span v-if="multiple ? !!modelValue?.length : !!modelValue" :class="ui.value()">
|
||||
{{ displayValue(modelValue as T, multiple) }}
|
||||
</span>
|
||||
<span v-else :class="ui.placeholder()">
|
||||
{{ placeholder ?? ' ' }}
|
||||
|
||||
@@ -14,7 +14,7 @@ describe('Input', () => {
|
||||
['with id', { props: { id: 'id' } }],
|
||||
['with name', { props: { name: 'name' } }],
|
||||
['with type', { props: { type: 'password' } }],
|
||||
['with placeholder', { props: { placeholder: 'Enter your username' } }],
|
||||
['with placeholder', { props: { placeholder: 'Search...' } }],
|
||||
['with disabled', { props: { disabled: true } }],
|
||||
['with required', { props: { required: true } }],
|
||||
['with icon', { props: { icon: 'i-heroicons-magnifying-glass' } }],
|
||||
|
||||
@@ -39,7 +39,7 @@ describe('Select', () => {
|
||||
['with defaultValue', { props: { ...props, defaultValue: items[0] } }],
|
||||
['with id', { props: { ...props, id: 'id' } }],
|
||||
['with name', { props: { ...props, name: 'name' } }],
|
||||
['with placeholder', { props: { ...props, placeholder: 'Enter your username' } }],
|
||||
['with placeholder', { props: { ...props, placeholder: 'Search...' } }],
|
||||
['with disabled', { props: { ...props, disabled: true } }],
|
||||
['with required', { props: { ...props, required: true } }],
|
||||
['with icon', { props: { ...props, icon: 'i-heroicons-magnifying-glass' } }],
|
||||
|
||||
@@ -37,9 +37,12 @@ describe('SelectMenu', () => {
|
||||
['with items', { props }],
|
||||
['with modelValue', { props: { ...props, modelValue: items[0] } }],
|
||||
['with defaultValue', { props: { ...props, defaultValue: items[0] } }],
|
||||
['with multiple', { props: { ...props, multiple: true } }],
|
||||
['with multiple and modelValue', { props: { ...props, multiple: true, modelValue: [items[0], items[1]] } }],
|
||||
['with id', { props: { ...props, id: 'id' } }],
|
||||
['with name', { props: { ...props, name: 'name' } }],
|
||||
['with placeholder', { props: { ...props, placeholder: 'Enter your username' } }],
|
||||
['with placeholder', { props: { ...props, placeholder: 'Search...' } }],
|
||||
['with searchPlaceholder', { props: { ...props, searchPlaceholder: 'Filter items...' } }],
|
||||
['with disabled', { props: { ...props, disabled: true } }],
|
||||
['with required', { props: { ...props, required: true } }],
|
||||
['with icon', { props: { ...props, icon: 'i-heroicons-magnifying-glass' } }],
|
||||
|
||||
@@ -107,7 +107,7 @@ exports[`Input > renders with name correctly 1`] = `
|
||||
`;
|
||||
|
||||
exports[`Input > renders with placeholder correctly 1`] = `
|
||||
"<div class="relative inline-flex items-center"><input type="text" placeholder="Enter your username" class="w-full rounded-md border-0 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 px-2.5 py-1.5 text-sm shadow-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white ring ring-inset ring-gray-300 dark:ring-gray-700 focus-visible:ring-2 focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400">
|
||||
"<div class="relative inline-flex items-center"><input type="text" placeholder="Search..." class="w-full rounded-md border-0 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 px-2.5 py-1.5 text-sm shadow-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white ring ring-inset ring-gray-300 dark:ring-gray-700 focus-visible:ring-2 focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400">
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>"
|
||||
|
||||
@@ -1287,7 +1287,7 @@ exports[`Select > renders with name correctly 1`] = `
|
||||
|
||||
exports[`Select > renders with placeholder correctly 1`] = `
|
||||
"<button role="combobox" type="button" aria-controls="radix-vue-select-content-36" aria-expanded="true" aria-required="false" aria-autocomplete="none" dir="ltr" data-state="open" data-placeholder="" class="relative group rounded-md inline-flex items-center focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 px-2.5 py-1.5 text-sm shadow-sm bg-white dark:bg-gray-900 text-gray-900 dark:text-white ring ring-inset ring-gray-300 dark:ring-gray-700 focus-visible:ring-2 focus-visible:ring-primary-500 dark:focus-visible:ring-primary-400 pr-9">
|
||||
<!--v-if--><span style="pointer-events: none;" class="truncate group-data-placeholder:text-current/50">Enter your username</span><span class="absolute inset-y-0 end-0 flex items-center pr-2.5"><span class="iconify i-heroicons:chevron-down-20-solid shrink-0 text-gray-400 dark:text-gray-500 size-5" aria-hidden="true"></span></span>
|
||||
<!--v-if--><span style="pointer-events: none;" class="truncate group-data-placeholder:text-current/50">Search...</span><span class="absolute inset-y-0 end-0 flex items-center pr-2.5"><span class="iconify i-heroicons:chevron-down-20-solid shrink-0 text-gray-400 dark:text-gray-500 size-5" aria-hidden="true"></span></span>
|
||||
</button>
|
||||
<!--teleport start-->
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user