Skip to content

Commit 63730d6

Browse files
feat(CommandPalette): add footer slot (#4457)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
1 parent 9ab184c commit 63730d6

File tree

8 files changed

+241
-2
lines changed

8 files changed

+241
-2
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<script setup lang="ts">
2+
const groups = [
3+
{
4+
id: 'actions',
5+
items: [
6+
{
7+
label: 'Add new file',
8+
suffix: 'Create a new file in the current directory',
9+
icon: 'i-lucide-file-plus',
10+
kbds: ['meta', 'N']
11+
},
12+
{
13+
label: 'Add new folder',
14+
suffix: 'Create a new folder in the current directory',
15+
icon: 'i-lucide-folder-plus',
16+
kbds: ['meta', 'F']
17+
},
18+
{
19+
label: 'Search files',
20+
suffix: 'Search across all files in the project',
21+
icon: 'i-lucide-search',
22+
kbds: ['meta', 'P']
23+
},
24+
{
25+
label: 'Settings',
26+
suffix: 'Open application settings',
27+
icon: 'i-lucide-settings',
28+
kbds: ['meta', ',']
29+
}
30+
]
31+
},
32+
{
33+
id: 'recent',
34+
label: 'Recent',
35+
items: [
36+
{
37+
label: 'project.vue',
38+
suffix: 'components/',
39+
icon: 'i-vscode-icons-file-type-vue'
40+
},
41+
{
42+
label: 'readme.md',
43+
suffix: 'docs/',
44+
icon: 'i-vscode-icons-file-type-markdown'
45+
},
46+
{
47+
label: 'package.json',
48+
suffix: 'root/',
49+
icon: 'i-vscode-icons-file-type-node'
50+
}
51+
]
52+
}
53+
]
54+
</script>
55+
56+
<template>
57+
<UCommandPalette :groups="groups" class="flex-1 h-80">
58+
<template #footer>
59+
<div class="flex items-center justify-between gap-2">
60+
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
61+
<div class="flex items-center gap-1">
62+
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
63+
<template #trailing>
64+
<UKbd value="enter" />
65+
</template>
66+
</UButton>
67+
<USeparator orientation="vertical" class="h-4" />
68+
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
69+
<template #trailing>
70+
<UKbd value="meta" />
71+
<UKbd value="k" />
72+
</template>
73+
</UButton>
74+
</div>
75+
</div>
76+
</template>
77+
</UCommandPalette>
78+
</template>

‎docs/content/3.components/command-palette.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,20 @@ props:
877877
This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example.
878878
::
879879

880+
### With footer slot :badge{label="Soon" class="align-text-top"}
881+
882+
Use the `#footer` slot to add custom content at the bottom of the CommandPalette, such as keyboard shortcuts help or additional actions.
883+
884+
::component-example
885+
---
886+
collapse: true
887+
name: 'command-palette-footer-slot-example'
888+
class: '!p-0'
889+
props:
890+
autofocus: false
891+
---
892+
::
893+
880894
### With custom slot
881895

882896
Use the `slot` property to customize a specific item or group.

‎playground/app/pages/components/command-palette.vue

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,27 @@ defineShortcuts({
166166
multiple
167167
class="sm:max-h-80"
168168
@update:model-value="onSelect"
169-
/>
169+
>
170+
<template #footer>
171+
<div class="flex items-center justify-between gap-2">
172+
<UIcon name="i-simple-icons-nuxtdotjs" class="size-5 text-dimmed ml-1" />
173+
<div class="flex items-center gap-1">
174+
<UButton color="neutral" variant="ghost" label="Open Command" class="text-dimmed" size="xs">
175+
<template #trailing>
176+
<UKbd value="enter" />
177+
</template>
178+
</UButton>
179+
<USeparator orientation="vertical" class="h-4" />
180+
<UButton color="neutral" variant="ghost" label="Actions" class="text-dimmed" size="xs">
181+
<template #trailing>
182+
<UKbd value="meta" />
183+
<UKbd value="k" />
184+
</template>
185+
</UButton>
186+
</div>
187+
</div>
188+
</template>
189+
</UCommandPalette>
170190
</DefineTemplate>
171191

172192
<div class="flex-1 flex flex-col gap-12 w-full max-w-lg">

‎src/runtime/components/CommandPalette.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ type SlotProps<T> = (props: { item: T, index: number }) => any
147147
148148
export type CommandPaletteSlots<G extends CommandPaletteGroup<T> = CommandPaletteGroup<any>, T extends CommandPaletteItem = CommandPaletteItem> = {
149149
'empty'(props: { searchTerm?: string }): any
150+
'footer'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
150151
'back'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
151152
'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
152153
'item': SlotProps<T>
@@ -444,5 +445,9 @@ function onSelect(e: Event, item: T) {
444445
</slot>
445446
</div>
446447
</ListboxContent>
448+
449+
<div v-if="!!slots.footer" :class="ui.footer({ class: props.ui?.footer })">
450+
<slot name="footer" :ui="ui" />
451+
</div>
447452
</ListboxRoot>
448453
</template>

‎src/theme/command-palette.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default (options: Required<ModuleOptions>) => ({
77
close: '',
88
back: 'p-0',
99
content: 'relative overflow-hidden flex flex-col',
10+
footer: 'p-1',
1011
viewport: 'relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none',
1112
group: 'p-1 isolate',
1213
empty: 'py-6 text-center text-sm text-muted',

‎test/components/CommandPalette.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ describe('CommandPalette', () => {
8989
['with item-label slot', { props, slots: { 'item-label': () => 'Item label slot' } }],
9090
['with item-trailing slot', { props, slots: { 'item-trailing': () => 'Item trailing slot' } }],
9191
['with custom slot', { props, slots: { custom: () => 'Custom slot' } }],
92-
['with close slot', { props: { ...props, close: true }, slots: { close: () => 'Close slot' } }]
92+
['with close slot', { props: { ...props, close: true }, slots: { close: () => 'Close slot' } }],
93+
['with footer slot', { props, slots: { footer: () => 'Footer slot' } }]
9394
])('renders %s correctly', async (nameOrHtml: string, options: { props?: CommandPaletteProps, slots?: Partial<CommandPaletteSlots> }) => {
9495
const html = await ComponentRender(nameOrHtml, options, CommandPalette)
9596
expect(html).toMatchSnapshot()

0 commit comments

Comments
 (0)