feat(InputMenu): new component (#1095)

This commit is contained in:
Benjamin Canac
2023-12-12 18:45:04 +01:00
committed by GitHub
parent 66a80c7486
commit 6d8d82a265
20 changed files with 852 additions and 57 deletions

View File

@@ -10,6 +10,7 @@ const options = [
const state = reactive({
input: undefined,
inputMenu: undefined,
textarea: undefined,
select: undefined,
selectMenu: undefined,
@@ -23,6 +24,9 @@ const state = reactive({
const schema = z.object({
input: z.string().min(10),
inputMenu: z.any().refine(option => option?.value === 'option-2', {
message: 'Select Option 2'
}),
textarea: z.string().min(10),
select: z.string().refine(value => value === 'option-2', {
message: 'Select Option 2'
@@ -61,6 +65,10 @@ async function onSubmit (event: FormSubmitEvent<Schema>) {
<UInput v-model="state.input" />
</UFormGroup>
<UFormGroup name="inputMenu" label="Input Menu">
<UInputMenu v-model="state.inputMenu" :options="options" />
</UFormGroup>
<UFormGroup name="textarea" label="Textarea">
<UTextarea v-model="state.textarea" />
</UFormGroup>

View File

@@ -0,0 +1,9 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[0])
</script>
<template>
<UInputMenu v-model="selected" :options="people" />
</template>

View File

@@ -0,0 +1,13 @@
<script setup>
const people = []
const selected = ref()
</script>
<template>
<UInputMenu v-model="selected" :options="people">
<template #empty>
No people
</template>
</UInputMenu>
</template>

View File

@@ -0,0 +1,36 @@
<script setup>
const people = [{
id: 'benjamincanac',
label: 'benjamincanac',
href: 'https://github.com/benjamincanac',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/739984?v=4' }
}, {
id: 'Atinux',
label: 'Atinux',
href: 'https://github.com/Atinux',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/904724?v=4' }
}, {
id: 'smarroufin',
label: 'smarroufin',
href: 'https://github.com/smarroufin',
target: '_blank',
avatar: { src: 'https://avatars.githubusercontent.com/u/7547335?v=4' }
}, {
id: 'nobody',
label: 'Nobody',
icon: 'i-heroicons-user-circle'
}]
const selected = ref(people[0])
</script>
<template>
<UInputMenu v-model="selected" :options="people">
<template #leading>
<UIcon v-if="selected.icon" :name="selected.icon" class="w-4 h-4 mx-0.5" />
<UAvatar v-else-if="selected.avatar" v-bind="selected.avatar" size="3xs" class="mx-0.5" />
</template>
</UInputMenu>
</template>

View File

@@ -0,0 +1,26 @@
<script setup>
const people = [{
id: 1,
name: 'Wade Cooper'
}, {
id: 2,
name: 'Arlene Mccoy'
}, {
id: 3,
name: 'Devon Webb'
}, {
id: 4,
name: 'Tom Cook'
}]
const selected = ref(people[0].name)
</script>
<template>
<UInputMenu
v-model="selected"
:options="people"
value-attribute="name"
option-attribute="name"
/>
</template>

View File

@@ -0,0 +1,13 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[0])
</script>
<template>
<UInputMenu v-model="selected" :options="people" searchable>
<template #option-empty="{ query }">
<q>{{ query }}</q> not found
</template>
</UInputMenu>
</template>

View File

@@ -0,0 +1,25 @@
<script setup>
const people = [
{ name: 'Wade Cooper', online: true },
{ name: 'Arlene Mccoy', online: false },
{ name: 'Devon Webb', online: false },
{ name: 'Tom Cook', online: true },
{ name: 'Tanya Fox', online: false },
{ name: 'Hellen Schmidt', online: true },
{ name: 'Caroline Schultz', online: true },
{ name: 'Mason Heaney', online: false },
{ name: 'Claudie Smitham', online: true },
{ name: 'Emil Schaefer', online: false }
]
const selected = ref(people[3])
</script>
<template>
<UInputMenu v-model="selected" :options="people" option-attribute="name">
<template #option="{ option: person }">
<span :class="[person.online ? 'bg-green-400' : 'bg-gray-200', 'inline-block h-2 w-2 flex-shrink-0 rounded-full']" aria-hidden="true" />
<span class="truncate">{{ person.name }}</span>
</template>
</UInputMenu>
</template>

View File

@@ -0,0 +1,9 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[0])
</script>
<template>
<UInputMenu v-model="selected" :options="people" :popper="{ arrow: true }" />
</template>

View File

@@ -0,0 +1,9 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[0])
</script>
<template>
<UInputMenu v-model="selected" :options="people" :popper="{ offsetDistance: 0 }" />
</template>

View File

@@ -0,0 +1,9 @@
<script setup>
const people = ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
const selected = ref(people[0])
</script>
<template>
<UInputMenu v-model="selected" :options="people" :popper="{ placement: 'right-start' }" />
</template>

View File

@@ -0,0 +1,28 @@
<script setup>
const options = [
{ id: 1, name: 'Wade Cooper', colors: ['red', 'yellow'] },
{ id: 2, name: 'Arlene Mccoy', colors: ['blue', 'yellow'] },
{ id: 3, name: 'Devon Webb', colors: ['green', 'blue'] },
{ id: 4, name: 'Tom Cook', colors: ['blue', 'red'] },
{ id: 5, name: 'Tanya Fox', colors: ['green', 'red'] },
{ id: 5, name: 'Hellen Schmidt', colors: ['green', 'yellow'] }
]
const selected = ref(options[1])
</script>
<template>
<UInputMenu
v-model="selected"
:options="options"
placeholder="Select a person"
by="id"
option-attribute="name"
:search-attributes="['name', 'colors']"
>
<template #option="{ option: person }">
<span v-for="color in person.colors" :key="color.id" class="h-2 w-2 rounded-full" :class="`bg-${color}-500 dark:bg-${color}-400`" />
<span class="truncate">{{ person.name }}</span>
</template>
</UInputMenu>
</template>

View File

@@ -172,13 +172,13 @@ Use the `#leading` slot to set the content of the leading icon.
::component-card
---
slots:
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" />
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5" />
baseProps:
placeholder: 'Search...'
---
#leading
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs"}
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5"}
::
### `trailing`

View File

@@ -0,0 +1,170 @@
---
title: InputMenu
description: Display an autocomplete input with real-time suggestions.
links:
- label: GitHub
icon: i-simple-icons-github
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/forms/InputMenu.vue
- label: 'Combobox'
icon: i-simple-icons-headlessui
to: 'https://headlessui.com/vue/combobox'
navigation:
badge: New
---
## Usage
The `InputMenu` component renders by default an [Input](/forms/input) component and is based on the `ui.input` preset. You can use most of the `Input` props to configure the display such as [color](/forms/input#style), [variant](/forms/input#style), [size](/forms/input#size), [placeholder](/forms/input#placeholder), [icon](/forms/input#icon), [disabled](/forms/input#disabled), etc.
You can use the `ui` prop like the `Input` component to override the default config. The `uiMenu` prop can be used to override the default menu config.
Pass an array of strings or objects to the `options` prop to display in the menu.
::component-example
---
component: 'input-menu-example-basic'
componentProps:
class: 'w-full lg:w-48'
---
::
::callout{icon="i-heroicons-exclamation-triangle"}
This component does not support multiple values. Use the [SelectMenu](/forms/select-menu#multiple) component instead.
::
### Objects
You can pass an array of objects to `options` and either compare on the whole object or use the `by` prop to compare on a specific key. You can configure which field will be used to display the label through the `option-attribute` prop that defaults to `label`.
::component-example
---
component: 'input-menu-example-objects'
componentProps:
class: 'w-full lg:w-48'
---
::
Use the `search-attributes` prop with an array of property names to search on each option object. Nested attributes can be accessed using `dot.notation`. When the property value is an array or object, these are cast to string so these can be searched within.
::component-example
---
component: 'input-menu-example-search-attributes'
componentProps:
class: 'w-full lg:w-48'
---
::
If you only want to select a single object property rather than the whole object as value, you can set the `value-attribute` property. This prop defaults to `null`.
::component-example
---
component: 'input-menu-example-objects-value-attribute'
componentProps:
class: 'w-full lg:w-48'
---
::
### Icon
The `InputMenu` has a button on the right to toggle the menu. Use the `trailing-icon` prop to set a different icon or change it globally in `ui.inputMenu.default.trailingIcon`. Defaults to `i-heroicons-chevron-down-20-solid`.
::component-card
---
baseProps:
class: 'w-full lg:w-48'
placeholder: 'Select a person'
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
props:
trailingIcon: 'i-heroicons-chevron-up-down-20-solid'
excludedProps:
- trailingIcon
---
::
Use the `selected-icon` prop to set a different icon or change it globally in `ui.inputMenu.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
::component-card
---
baseProps:
class: 'w-full lg:w-48'
placeholder: 'Select a person'
options: ['Wade Cooper', 'Arlene Mccoy', 'Devon Webb', 'Tom Cook', 'Tanya Fox', 'Hellen Schmidt', 'Caroline Schultz', 'Mason Heaney', 'Claudie Smitham', 'Emil Schaefer']
props:
selectedIcon: 'i-heroicons-hand-thumb-up-solid'
excludedProps:
- selectedIcon
---
::
::callout{icon="i-heroicons-light-bulb"}
Learn how to customize icons from the [Input](/forms/input#icon) component.
::
## Popper
Use the `popper` prop to customize the popper instance.
### Arrow
:component-example{component="input-menu-example-popper-arrow"}
### Placement
:component-example{component="input-menu-example-popper-placement"}
### Offset
:component-example{component="input-menu-example-popper-offset"}
## Slots
### `option`
Use the `#option` slot to customize the option content. You will have access to the `option`, `active` and `selected` properties in the slot scope.
::component-example
---
component: 'input-menu-example-option-slot'
componentProps:
class: 'w-full lg:w-48'
---
::
### `option-empty`
Use the `#option-empty` slot to customize the content displayed when the `searchable` prop is `true` and there is no options. You will have access to the `query` property in the slot scope.
::component-example
---
component: 'input-menu-example-option-empty-slot'
componentProps:
class: 'w-full lg:w-48'
---
::
### `empty`
Use the `#empty` slot to customize the content displayed when there is no options. Defaults to `No options.`.
::component-example
---
component: 'input-menu-example-empty-slot'
componentProps:
class: 'w-full lg:w-48'
---
::
## Props
:component-props
## Config
::callout{icon="i-heroicons-light-bulb"}
Use the `ui` prop to override the input config and the `uiMenu` prop to override the menu config.
::
::tabs{:selectedIndex="1"}
:component-preset{label="Input (ui)" slug="Input"}
:component-preset{label="InputMenu (uiMenu)"}
::

View File

@@ -203,7 +203,7 @@ Use the `#leading` slot to set the content of the leading icon.
::component-card
---
slots:
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" />
leading: <UAvatar src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5" />
baseProps:
options:
- 'United States'
@@ -213,7 +213,7 @@ baseProps:
---
#leading
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs"}
:u-avatar{src="https://avatars.githubusercontent.com/u/739984?v=4" size="3xs" class="mx-0.5"}
::
### `trailing`