mirror of
https://github.com/ArthurDanjou/ui.git
synced 2026-01-14 12:14:41 +01:00
391 lines
10 KiB
Markdown
391 lines
10 KiB
Markdown
---
|
|
title: CommandPalette
|
|
description: Add a customizable command palette to your app.
|
|
links:
|
|
- label: GitHub
|
|
icon: i-simple-icons-github
|
|
to: https://github.com/nuxt/ui/blob/dev/src/runtime/components/navigation/CommandPalette.vue
|
|
- label: 'Combobox'
|
|
icon: i-simple-icons-headlessui
|
|
to: 'https://headlessui.com/vue/combobox'
|
|
---
|
|
|
|
## Usage
|
|
|
|
Use a `v-model` to display a searchable and selectable list of commands.
|
|
|
|
::component-example
|
|
---
|
|
padding: false
|
|
---
|
|
|
|
#default
|
|
:command-palette-example-basic{class="h-[257px]"}
|
|
|
|
#code
|
|
```vue
|
|
<script setup>
|
|
const people = [
|
|
{ id: 1, label: 'Wade Cooper' },
|
|
{ id: 2, label: 'Arlene Mccoy' },
|
|
{ id: 3, label: 'Devon Webb' },
|
|
{ id: 4, label: 'Tom Cook' },
|
|
{ id: 5, label: 'Tanya Fox' },
|
|
{ id: 6, label: 'Hellen Schmidt' },
|
|
{ id: 7, label: 'Caroline Schultz' },
|
|
{ id: 8, label: 'Mason Heaney' },
|
|
{ id: 9, label: 'Claudie Smitham' },
|
|
{ id: 10, label: 'Emil Schaefer' }
|
|
]
|
|
|
|
const selected = ref([people[3]])
|
|
</script>
|
|
|
|
<template>
|
|
<UCommandPalette
|
|
v-model="selected"
|
|
multiple
|
|
nullable
|
|
:groups="[{ key: 'people', commands: people }]"
|
|
:fuse="{ resultLimit: 6, fuseOptions: { threshold: 0.1 } }"
|
|
/>
|
|
</template>
|
|
```
|
|
::
|
|
|
|
You can put a `CommandPalette` anywhere you want but it's most commonly used inside of a modal.
|
|
|
|
::component-example
|
|
#default
|
|
:command-palette-example-modal
|
|
|
|
#code
|
|
```vue
|
|
<script setup>
|
|
const isOpen = ref(false)
|
|
|
|
const people = [
|
|
{ id: 1, label: 'Wade Cooper' },
|
|
{ id: 2, label: 'Arlene Mccoy' },
|
|
{ id: 3, label: 'Devon Webb' },
|
|
{ id: 4, label: 'Tom Cook' },
|
|
{ id: 5, label: 'Tanya Fox' },
|
|
{ id: 6, label: 'Hellen Schmidt' },
|
|
{ id: 7, label: 'Caroline Schultz' },
|
|
{ id: 8, label: 'Mason Heaney' },
|
|
{ id: 9, label: 'Claudie Smitham' },
|
|
{ id: 10, label: 'Emil Schaefer' }
|
|
]
|
|
|
|
const selected = ref([])
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<UButton label="Open" @click="isOpen = true" />
|
|
|
|
<UModal v-model="isOpen">
|
|
<UCommandPalette
|
|
v-model="selected"
|
|
multiple
|
|
nullable
|
|
:groups="[{ key: 'people', commands: people }]"
|
|
/>
|
|
</UModal>
|
|
</div>
|
|
</template>
|
|
```
|
|
::
|
|
|
|
You can pass multiple groups of commands to the component. Each group will be separated by a divider and will display a label.
|
|
|
|
Without a `v-model`, you can also listen on `@update:model-value` to navigate to a link or do something else when a command is clicked.
|
|
|
|
::component-example
|
|
---
|
|
padding: false
|
|
---
|
|
|
|
#default
|
|
:command-palette-example-groups{class="h-[274px]"}
|
|
|
|
#code
|
|
```vue
|
|
<script setup>
|
|
const router = useRouter()
|
|
const toast = useToast()
|
|
|
|
const commandPaletteRef = ref()
|
|
|
|
const users = [
|
|
{ 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' } }
|
|
]
|
|
|
|
const actions = [
|
|
{ id: 'new-file', label: 'Add new file', icon: 'i-heroicons-document-plus', click: () => toast.add({ title: 'New file added!' }), shortcuts: ['⌘', 'N'] },
|
|
{ id: 'new-folder', label: 'Add new folder', icon: 'i-heroicons-folder-plus', click: () => toast.add({ title: 'New folder added!' }), shortcuts: ['⌘', 'F'] },
|
|
{ id: 'hashtag', label: 'Add hashtag', icon: 'i-heroicons-hashtag', click: () => toast.add({ title: 'Hashtag added!' }), shortcuts: ['⌘', 'H'] },
|
|
{ id: 'label', label: 'Add label', icon: 'i-heroicons-tag', click: () => toast.add({ title: 'Label added!' }), shortcuts: ['⌘', 'L'] }
|
|
]
|
|
|
|
const groups = computed(() =>
|
|
[commandPaletteRef.value?.query ? {
|
|
key: 'users',
|
|
commands: users
|
|
} : {
|
|
key: 'recent',
|
|
label: 'Recent searches',
|
|
commands: users.slice(0, 1)
|
|
}, {
|
|
key: 'actions',
|
|
commands: actions
|
|
}].filter(Boolean))
|
|
|
|
function onSelect (option) {
|
|
if (option.click) {
|
|
option.click()
|
|
} else if (option.to) {
|
|
router.push(option.to)
|
|
} else if (option.href) {
|
|
window.open(option.href, '_blank')
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UCommandPalette ref="commandPaletteRef" :groups="groups" @update:model-value="onSelect" />
|
|
</template>
|
|
```
|
|
::
|
|
|
|
### Icon
|
|
|
|
Use any icon from [Iconify](https://icones.js.org) by setting the `icon` prop by using this pattern: `i-{collection_name}-{icon_name}`.
|
|
|
|
Use the `selected-icon` prop to set a different icon or change it globally in `ui.commandPalette.default.selectedIcon`. Defaults to `i-heroicons-check-20-solid`.
|
|
|
|
::component-card
|
|
---
|
|
padding: false
|
|
baseProps:
|
|
emptyState: null
|
|
props:
|
|
icon: 'i-heroicons-command-line'
|
|
excludedProps:
|
|
- icon
|
|
---
|
|
::
|
|
|
|
### Loading
|
|
|
|
Use the `loading` prop to show a loading icon.
|
|
|
|
Use the `loading-icon` prop to set a different icon or change it globally in `ui.commandPalette.default.loadingIcon`. Defaults to `i-heroicons-arrow-path-20-solid`.
|
|
|
|
::component-card
|
|
---
|
|
padding: false
|
|
baseProps:
|
|
emptyState: null
|
|
props:
|
|
loading: true
|
|
excludedProps:
|
|
- icon
|
|
---
|
|
::
|
|
|
|
### Placeholder
|
|
|
|
Use the `placeholder` prop to change the input placeholder
|
|
|
|
::component-card
|
|
---
|
|
padding: false
|
|
baseProps:
|
|
emptyState: null
|
|
props:
|
|
placeholder: 'Type a command...'
|
|
excludedProps:
|
|
- icon
|
|
---
|
|
::
|
|
|
|
### Close
|
|
|
|
Use the `close-button` prop to display a close button on the right side of the input.
|
|
|
|
You can pass all the props of the [Button](/elements/button) component to customize it through the `close-button` prop or globally through `ui.commandPalette.default.closeButton`.
|
|
|
|
::component-card
|
|
---
|
|
padding: false
|
|
baseProps:
|
|
emptyState: null
|
|
props:
|
|
closeButton:
|
|
icon: 'i-heroicons-x-mark-20-solid'
|
|
color: 'gray'
|
|
variant: 'link'
|
|
padded: false
|
|
excludedProps:
|
|
- closeButton
|
|
---
|
|
::
|
|
|
|
### Empty
|
|
|
|
An empty state will be displayed when there are no results.
|
|
|
|
Use the `empty-state` prop to customize the `icon` and `label` or change them globally in `ui.commandPalette.default.emptyState`.
|
|
|
|
You can also set it to `null` to hide the empty state.
|
|
|
|
::component-card
|
|
---
|
|
padding: false
|
|
baseProps:
|
|
placeholder: 'Type something to see the empty label change'
|
|
props:
|
|
emptyState:
|
|
icon: 'i-heroicons-magnifying-glass-20-solid'
|
|
label: "We couldn't find any items."
|
|
queryLabel: "We couldn't find any items with that term. Please try again."
|
|
excludedProps:
|
|
- emptyState
|
|
---
|
|
::
|
|
|
|
## Full-text search
|
|
|
|
The CommandPalette component takes care of the full-text search for you with [Fuse.js](https://fusejs.io). You can pass all the options of Fuse.js through the `fuse` prop.
|
|
|
|
When searching for a command, the component will look for a `label` property on the command by default. You can customize this behavior by overriding the `command-attribute` prop. This will also affect the display of the command.
|
|
|
|
You can also highlight the matches in the command by setting the `fuse.fuseOptions.includeMatches` to `true`. The CommandPalette component automatically takes care of the highlighting for you.
|
|
|
|
```vue
|
|
<template>
|
|
<UCommandPalette
|
|
command-attribute="title"
|
|
:fuse="{
|
|
fuseOptions: {
|
|
ignoreLocation: true,
|
|
includeMatches: true,
|
|
threshold: 0,
|
|
keys: ['title', 'description', 'children.children.value', 'children.children.children.value']
|
|
},
|
|
resultLimit: 10
|
|
}"
|
|
/>
|
|
</template>
|
|
```
|
|
|
|
::callout{icon="i-heroicons-light-bulb"}
|
|
Try it yourself in this documentation's search by pressing :kbd{value="meta"} :kbd{value="K" class="ml-1"}.
|
|
::
|
|
|
|
## Async search
|
|
|
|
You can also pass an `async` function to the `search` property of a group to perform an async search. The function will receive the query as its first argument and should return an array of commands.
|
|
|
|
::component-example
|
|
---
|
|
padding: false
|
|
---
|
|
|
|
#default
|
|
:command-palette-example-async{class="h-[274px]"}
|
|
#code
|
|
```vue
|
|
<script setup>
|
|
const groups = computed(() => {
|
|
return [{
|
|
key: 'users',
|
|
label: q => q && `Users matching “${q}”...`,
|
|
search: async (q) => {
|
|
if (!q) {
|
|
return []
|
|
}
|
|
|
|
const users = await $fetch(`https://jsonplaceholder.typicode.com/users`, { params: { q } })
|
|
|
|
return users.map(user => ({ id: user.id, label: user.name, suffix: user.email }))
|
|
}
|
|
}].filter(Boolean)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<UCommandPalette :groups="groups" />
|
|
</template>
|
|
```
|
|
::
|
|
|
|
::callout{icon="i-heroicons-light-bulb"}
|
|
The `loading` state will automatically be enabled when a `search` function is loading. You can disable this behavior by setting the `loading-icon` prop to `null` or globally in `ui.commandPalette.default.loadingIcon`.
|
|
::
|
|
|
|
## Slots
|
|
|
|
### `<group>-icon`
|
|
|
|
Use the `#<group>-icon` slot to override the left command content which display by default the `icon`, `avatar` and `chip`.
|
|
|
|
### `<group>-command`
|
|
|
|
Use the `#<group>-command` slot to override the command content which display by default the `prefix`, `suffix` and `label` (customizable through the `command-attribute` prop).
|
|
|
|
### `<group>-active`
|
|
|
|
Use the `#<group>-active` slot to override the right command content (when hovered) which display by default the `active` field of the group if provided.
|
|
|
|
### `<group>-inactive`
|
|
|
|
Use the `#<group>-inactive` slot to override the right command content (when not hovered) which display by default the `inactive` field of the group if provided or the `shortcuts` of the command.
|
|
|
|
::callout{icon="i-heroicons-light-bulb"}
|
|
The 4 slots above will have access to the `group`, `command`, `active` and `selected` properties in the slot scope.
|
|
::
|
|
|
|
### `empty-state`
|
|
|
|
Use the `#empty-state` slot to customize the empty state.
|
|
|
|
::component-example{class="grid"}
|
|
---
|
|
padding: false
|
|
overflowClass: 'overflow-x-auto'
|
|
---
|
|
|
|
#default
|
|
:command-palette-example-empty-slot{class="flex-1"}
|
|
|
|
#code
|
|
```vue
|
|
<script setup>
|
|
const groups = [...]
|
|
</script>
|
|
|
|
<template>
|
|
<UCommandPalette :groups="groups">
|
|
<template #empty-state>
|
|
<div class="flex flex-col items-center justify-center py-6 gap-3">
|
|
<span class="italic text-sm">Nothing here!</span>
|
|
<UButton label="Add item" />
|
|
</div>
|
|
</template>
|
|
</UCommandPalette>
|
|
</template>
|
|
```
|
|
::
|
|
|
|
## Props
|
|
|
|
:component-props
|
|
|
|
## Preset
|
|
|
|
:component-preset
|