From 63730d684b4ab652b66b4b1a4daf664d434e2f58 Mon Sep 17 00:00:00 2001 From: Hugo Richard Date: Mon, 7 Jul 2025 12:01:26 +0200 Subject: [PATCH] feat(CommandPalette): add `footer` slot (#4457) Co-authored-by: Benjamin Canac --- .../CommandPaletteFooterSlotExample.vue | 78 +++++++++++++++++++ docs/content/3.components/command-palette.md | 14 ++++ .../app/pages/components/command-palette.vue | 22 +++++- src/runtime/components/CommandPalette.vue | 5 ++ src/theme/command-palette.ts | 1 + test/components/CommandPalette.spec.ts | 3 +- .../CommandPalette-vue.spec.ts.snap | 60 ++++++++++++++ .../__snapshots__/CommandPalette.spec.ts.snap | 60 ++++++++++++++ 8 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 docs/app/components/content/examples/command-palette/CommandPaletteFooterSlotExample.vue diff --git a/docs/app/components/content/examples/command-palette/CommandPaletteFooterSlotExample.vue b/docs/app/components/content/examples/command-palette/CommandPaletteFooterSlotExample.vue new file mode 100644 index 00000000..18aef1a8 --- /dev/null +++ b/docs/app/components/content/examples/command-palette/CommandPaletteFooterSlotExample.vue @@ -0,0 +1,78 @@ + + + diff --git a/docs/content/3.components/command-palette.md b/docs/content/3.components/command-palette.md index a37fc9a5..6f58f421 100644 --- a/docs/content/3.components/command-palette.md +++ b/docs/content/3.components/command-palette.md @@ -877,6 +877,20 @@ props: This can be useful when using the CommandPalette inside a [`Modal`](/components/modal) for example. :: +### With footer slot :badge{label="Soon" class="align-text-top"} + +Use the `#footer` slot to add custom content at the bottom of the CommandPalette, such as keyboard shortcuts help or additional actions. + +::component-example +--- +collapse: true +name: 'command-palette-footer-slot-example' +class: '!p-0' +props: + autofocus: false +--- +:: + ### With custom slot Use the `slot` property to customize a specific item or group. diff --git a/playground/app/pages/components/command-palette.vue b/playground/app/pages/components/command-palette.vue index 6b06a24e..5dd4de26 100644 --- a/playground/app/pages/components/command-palette.vue +++ b/playground/app/pages/components/command-palette.vue @@ -166,7 +166,27 @@ defineShortcuts({ multiple class="sm:max-h-80" @update:model-value="onSelect" - /> + > + +
diff --git a/src/runtime/components/CommandPalette.vue b/src/runtime/components/CommandPalette.vue index 0fa7476b..73c7a23c 100644 --- a/src/runtime/components/CommandPalette.vue +++ b/src/runtime/components/CommandPalette.vue @@ -147,6 +147,7 @@ type SlotProps = (props: { item: T, index: number }) => any export type CommandPaletteSlots = CommandPaletteGroup, T extends CommandPaletteItem = CommandPaletteItem> = { 'empty'(props: { searchTerm?: string }): any + 'footer'(props: { ui: { [K in keyof Required]: (props?: Record) => string } }): any 'back'(props: { ui: { [K in keyof Required]: (props?: Record) => string } }): any 'close'(props: { ui: { [K in keyof Required]: (props?: Record) => string } }): any 'item': SlotProps @@ -444,5 +445,9 @@ function onSelect(e: Event, item: T) {
+ +
+ +
diff --git a/src/theme/command-palette.ts b/src/theme/command-palette.ts index 4e72a82a..47779df3 100644 --- a/src/theme/command-palette.ts +++ b/src/theme/command-palette.ts @@ -7,6 +7,7 @@ export default (options: Required) => ({ close: '', back: 'p-0', content: 'relative overflow-hidden flex flex-col', + footer: 'p-1', viewport: 'relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none', group: 'p-1 isolate', empty: 'py-6 text-center text-sm text-muted', diff --git a/test/components/CommandPalette.spec.ts b/test/components/CommandPalette.spec.ts index 3fb72741..9a37c3dd 100644 --- a/test/components/CommandPalette.spec.ts +++ b/test/components/CommandPalette.spec.ts @@ -89,7 +89,8 @@ describe('CommandPalette', () => { ['with item-label slot', { props, slots: { 'item-label': () => 'Item label slot' } }], ['with item-trailing slot', { props, slots: { 'item-trailing': () => 'Item trailing slot' } }], ['with custom slot', { props, slots: { custom: () => 'Custom slot' } }], - ['with close slot', { props: { ...props, close: true }, slots: { close: () => 'Close slot' } }] + ['with close slot', { props: { ...props, close: true }, slots: { close: () => 'Close slot' } }], + ['with footer slot', { props, slots: { footer: () => 'Footer slot' } }] ])('renders %s correctly', async (nameOrHtml: string, options: { props?: CommandPaletteProps, slots?: Partial }) => { const html = await ComponentRender(nameOrHtml, options, CommandPalette) expect(html).toMatchSnapshot() diff --git a/test/components/__snapshots__/CommandPalette-vue.spec.ts.snap b/test/components/__snapshots__/CommandPalette-vue.spec.ts.snap index a29f3fdb..8cd26c80 100644 --- a/test/components/__snapshots__/CommandPalette-vue.spec.ts.snap +++ b/test/components/__snapshots__/CommandPalette-vue.spec.ts.snap @@ -32,6 +32,7 @@ exports[`CommandPalette > renders with as correctly 1`] = ` + " `; @@ -68,6 +69,7 @@ exports[`CommandPalette > renders with class correctly 1`] = ` + " `; @@ -102,6 +104,7 @@ exports[`CommandPalette > renders with close correctly 1`] = ` + " `; @@ -136,6 +139,7 @@ exports[`CommandPalette > renders with close slot correctly 1`] = ` + " `; @@ -170,6 +174,7 @@ exports[`CommandPalette > renders with closeIcon correctly 1`] = ` + " `; @@ -204,6 +209,7 @@ exports[`CommandPalette > renders with custom slot correctly 1`] = ` + " `; @@ -240,6 +246,7 @@ exports[`CommandPalette > renders with defaultValue correctly 1`] = ` + " `; @@ -276,6 +283,7 @@ exports[`CommandPalette > renders with disabled correctly 1`] = ` + " `; @@ -312,6 +320,44 @@ exports[`CommandPalette > renders with empty slot correctly 1`] = ` + + +" +`; + +exports[`CommandPalette > renders with footer slot correctly 1`] = ` +"
+
+ +
+
+ +
+
Footer slot
" `; @@ -348,6 +394,7 @@ exports[`CommandPalette > renders with groups correctly 1`] = ` + " `; @@ -384,6 +431,7 @@ exports[`CommandPalette > renders with icon correctly 1`] = ` + " `; @@ -408,6 +456,7 @@ exports[`CommandPalette > renders with item slot correctly 1`] = ` + " `; @@ -444,6 +493,7 @@ exports[`CommandPalette > renders with item-label slot correctly 1`] = ` + " `; @@ -474,6 +524,7 @@ exports[`CommandPalette > renders with item-leading slot correctly 1`] = ` + " `; @@ -504,6 +555,7 @@ exports[`CommandPalette > renders with item-trailing slot correctly 1`] = ` + " `; @@ -545,6 +597,7 @@ exports[`CommandPalette > renders with labelKey correctly 1`] = ` + " `; @@ -581,6 +634,7 @@ exports[`CommandPalette > renders with loading correctly 1`] = ` + " `; @@ -617,6 +671,7 @@ exports[`CommandPalette > renders with loadingIcon correctly 1`] = ` + " `; @@ -653,6 +708,7 @@ exports[`CommandPalette > renders with modelValue correctly 1`] = ` + " `; @@ -689,6 +745,7 @@ exports[`CommandPalette > renders with placeholder correctly 1`] = ` + " `; @@ -725,6 +782,7 @@ exports[`CommandPalette > renders with selectedIcon correctly 1`] = ` + " `; @@ -761,6 +819,7 @@ exports[`CommandPalette > renders with ui correctly 1`] = ` + " `; @@ -773,6 +832,7 @@ exports[`CommandPalette > renders without data correctly 1`] = `
No data
+ " `; diff --git a/test/components/__snapshots__/CommandPalette.spec.ts.snap b/test/components/__snapshots__/CommandPalette.spec.ts.snap index 067b2727..101159fb 100644 --- a/test/components/__snapshots__/CommandPalette.spec.ts.snap +++ b/test/components/__snapshots__/CommandPalette.spec.ts.snap @@ -32,6 +32,7 @@ exports[`CommandPalette > renders with as correctly 1`] = ` + " `; @@ -68,6 +69,7 @@ exports[`CommandPalette > renders with class correctly 1`] = ` + " `; @@ -105,6 +107,7 @@ exports[`CommandPalette > renders with close correctly 1`] = ` + " `; @@ -139,6 +142,7 @@ exports[`CommandPalette > renders with close slot correctly 1`] = ` + " `; @@ -176,6 +180,7 @@ exports[`CommandPalette > renders with closeIcon correctly 1`] = ` + " `; @@ -210,6 +215,7 @@ exports[`CommandPalette > renders with custom slot correctly 1`] = ` + " `; @@ -246,6 +252,7 @@ exports[`CommandPalette > renders with defaultValue correctly 1`] = ` + " `; @@ -282,6 +289,7 @@ exports[`CommandPalette > renders with disabled correctly 1`] = ` + " `; @@ -318,6 +326,44 @@ exports[`CommandPalette > renders with empty slot correctly 1`] = ` + + +" +`; + +exports[`CommandPalette > renders with footer slot correctly 1`] = ` +"
+
+ +
+
+ +
+
Footer slot
" `; @@ -354,6 +400,7 @@ exports[`CommandPalette > renders with groups correctly 1`] = ` + " `; @@ -390,6 +437,7 @@ exports[`CommandPalette > renders with icon correctly 1`] = ` + " `; @@ -414,6 +462,7 @@ exports[`CommandPalette > renders with item slot correctly 1`] = ` + " `; @@ -450,6 +499,7 @@ exports[`CommandPalette > renders with item-label slot correctly 1`] = ` + " `; @@ -480,6 +530,7 @@ exports[`CommandPalette > renders with item-leading slot correctly 1`] = ` + " `; @@ -510,6 +561,7 @@ exports[`CommandPalette > renders with item-trailing slot correctly 1`] = ` + " `; @@ -551,6 +603,7 @@ exports[`CommandPalette > renders with labelKey correctly 1`] = ` + " `; @@ -587,6 +640,7 @@ exports[`CommandPalette > renders with loading correctly 1`] = ` + " `; @@ -623,6 +677,7 @@ exports[`CommandPalette > renders with loadingIcon correctly 1`] = ` + " `; @@ -659,6 +714,7 @@ exports[`CommandPalette > renders with modelValue correctly 1`] = ` + " `; @@ -695,6 +751,7 @@ exports[`CommandPalette > renders with placeholder correctly 1`] = ` + " `; @@ -731,6 +788,7 @@ exports[`CommandPalette > renders with selectedIcon correctly 1`] = ` + " `; @@ -767,6 +825,7 @@ exports[`CommandPalette > renders with ui correctly 1`] = ` + " `; @@ -779,6 +838,7 @@ exports[`CommandPalette > renders without data correctly 1`] = `
No data
+ " `;