This commit is contained in:
HugoRCD
2025-07-03 17:07:16 +02:00
parent 7be03e0f51
commit c82828d889
5 changed files with 30 additions and 24 deletions

View File

@@ -157,6 +157,7 @@ defineShortcuts({
v-model="selected"
v-model:search-term="searchTerm"
:loading="status === 'pending'"
trailing-icon="i-lucide-settings"
:groups="groups"
:fuse="{
fuseOptions: {

View File

@@ -80,7 +80,7 @@ export interface CommandPaletteProps<G extends CommandPaletteGroup<T> = CommandP
* @defaultValue appConfig.ui.icons.chevronRight
* @IconifyIcon
*/
trailingIcon?: string
itemTrailingIcon?: string
/**
* The placeholder text for the input.
* @defaultValue t('commandPalette.placeholder')
@@ -91,6 +91,12 @@ export interface CommandPaletteProps<G extends CommandPaletteGroup<T> = CommandP
* @defaultValue true
*/
autofocus?: boolean
/**
* The icon displayed in the input.
* @defaultValue appConfig.ui.icons.search
* @IconifyIcon
*/
trailingIcon?: string
/**
* Display a close button in the input (useful when inside a Modal for example).
* `{ size: 'md', color: 'neutral', variant: 'ghost' }`{lang="ts-type"}
@@ -116,11 +122,6 @@ export interface CommandPaletteProps<G extends CommandPaletteGroup<T> = CommandP
* @IconifyIcon
*/
backIcon?: string
/**
* Display a trailing icon in the input.
* @IconifyIcon
*/
inputTrailingIcon?: string
groups?: G[]
/**
* Options for [useFuse](https://vueuse.org/integrations/useFuse).
@@ -153,8 +154,9 @@ type SlotProps<T> = (props: { item: T, index: number }) => any
export type CommandPaletteSlots<G extends CommandPaletteGroup<T> = CommandPaletteGroup<any>, T extends CommandPaletteItem = CommandPaletteItem> = {
'empty'(props: { searchTerm?: string }): any
'back'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
'trailing'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
'actions'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
'input-trailing'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
'item': SlotProps<T>
'item-leading': SlotProps<T>
'item-label': SlotProps<T>
@@ -342,6 +344,7 @@ function onSelect(e: Event, item: T) {
:autofocus="autofocus"
v-bind="inputProps"
:icon="icon || appConfig.ui.icons.search"
:trailing-icon="trailingIcon"
:class="ui.input({ class: props.ui?.input })"
@keydown.backspace="onBackspace"
>
@@ -359,15 +362,11 @@ function onSelect(e: Event, item: T) {
</slot>
</template>
<template v-if="inputTrailingIcon || !!slots['input-trailing'] || close || !!slots.close" #trailing>
<slot name="input-trailing" :ui="ui">
<UIcon
v-if="inputTrailingIcon"
:name="inputTrailingIcon"
class="shrink-0 size-5 text-dimmed"
/>
<template v-if="trailingIcon || !!slots.trailing || close || !!slots.close || !!slots.actions" #trailing>
<slot name="trailing" :ui="ui">
<UIcon v-if="trailingIcon" :name="trailingIcon" :class="ui.trailingIcon({ class: props.ui?.trailingIcon })" />
</slot>
<slot name="actions" :ui="ui" />
<slot name="close" :ui="ui">
<UButton
v-if="close"
@@ -430,7 +429,7 @@ function onSelect(e: Event, item: T) {
<slot :name="((item.slot ? `${item.slot}-trailing` : group.slot ? `${group.slot}-trailing` : `item-trailing`) as keyof CommandPaletteSlots<G, T>)" :item="(item as any)" :index="index">
<UIcon
v-if="item.children && item.children.length > 0"
:name="trailingIcon || appConfig.ui.icons.chevronRight"
:name="itemTrailingIcon || appConfig.ui.icons.chevronRight"
:class="ui.itemTrailingIcon({ class: [props.ui?.itemTrailingIcon, item.ui?.itemTrailingIcon] })"
/>

View File

@@ -6,7 +6,7 @@ export default (options: Required<ModuleOptions>) => ({
input: '[&>input]:h-12',
close: '',
back: 'p-0',
inputTrailingIcon: 'shrink-0 size-5 text-dimmed',
trailingIcon: 'shrink-0 size-5 text-dimmed',
content: 'relative overflow-hidden flex flex-col',
viewport: 'relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none',
group: 'p-1 isolate',

View File

@@ -390,7 +390,9 @@ exports[`CommandPalette > renders with icon correctly 1`] = `
exports[`CommandPalette > renders with input-trailing slot correctly 1`] = `
"<div dir="ltr" class="flex flex-col min-h-0 min-w-0 divide-y divide-default">
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-md border-0 placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9 pe-9" autocomplete="off" aria-disabled="false" value=""><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 text-dimmed size-5"></svg></span><span class="absolute inset-y-0 end-0 flex items-center pe-2.5">Input trailing slot<!--v-if--></span></div>
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-md border-0 placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9" autocomplete="off" aria-disabled="false" value=""><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 text-dimmed size-5"></svg></span>
<!--v-if-->
</div>
<div class="relative overflow-hidden flex flex-col" role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical">
<div role="presentation" class="relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none">
<div role="group" aria-labelledby="reka-listbox-group-v-0" class="p-1 isolate">
@@ -423,8 +425,10 @@ exports[`CommandPalette > renders with input-trailing slot correctly 1`] = `
`;
exports[`CommandPalette > renders with inputTrailingIcon correctly 1`] = `
"<div dir="ltr" class="flex flex-col min-h-0 min-w-0 divide-y divide-default">
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-md border-0 placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9 pe-9" autocomplete="off" aria-disabled="false" value=""><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 text-dimmed size-5"></svg></span><span class="absolute inset-y-0 end-0 flex items-center pe-2.5"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 size-5 text-dimmed"></svg><!--v-if--></span></div>
"<div dir="ltr" class="flex flex-col min-h-0 min-w-0 divide-y divide-default" inputtrailingicon="i-lucide-settings">
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-md border-0 placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9" autocomplete="off" aria-disabled="false" value=""><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" viewBox="0 0 16 16" class="shrink-0 text-dimmed size-5"></svg></span>
<!--v-if-->
</div>
<div class="relative overflow-hidden flex flex-col" role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical">
<div role="presentation" class="relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none">
<div role="group" aria-labelledby="reka-listbox-group-v-0" class="p-1 isolate">

View File

@@ -396,7 +396,9 @@ exports[`CommandPalette > renders with icon correctly 1`] = `
exports[`CommandPalette > renders with input-trailing slot correctly 1`] = `
"<div dir="ltr" class="flex flex-col min-h-0 min-w-0 divide-y divide-default">
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-md border-0 placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9 pe-9" autocomplete="off" aria-disabled="false" value="" aria-activedescendant="reka-listbox-item-v-0-0-1"><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><span class="iconify i-lucide:search shrink-0 text-dimmed size-5" aria-hidden="true"></span></span><span class="absolute inset-y-0 end-0 flex items-center pe-2.5">Input trailing slot<!--v-if--></span></div>
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-md border-0 placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9" autocomplete="off" aria-disabled="false" value="" aria-activedescendant="reka-listbox-item-v-0-0-1"><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><span class="iconify i-lucide:search shrink-0 text-dimmed size-5" aria-hidden="true"></span></span>
<!--v-if-->
</div>
<div class="relative overflow-hidden flex flex-col" role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical">
<div role="presentation" class="relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none">
<div role="group" aria-labelledby="reka-listbox-group-v-0-0-0" class="p-1 isolate">
@@ -429,9 +431,9 @@ exports[`CommandPalette > renders with input-trailing slot correctly 1`] = `
`;
exports[`CommandPalette > renders with inputTrailingIcon correctly 1`] = `
"<div dir="ltr" class="flex flex-col min-h-0 min-w-0 divide-y divide-default">
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-md border-0 placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9 pe-9" autocomplete="off" aria-disabled="false" value="" aria-activedescendant="reka-listbox-item-v-0-0-1"><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><span class="iconify i-lucide:search shrink-0 text-dimmed size-5" aria-hidden="true"></span></span><span class="absolute inset-y-0 end-0 flex items-center pe-2.5"><span class="iconify i-lucide:settings shrink-0 size-5 text-dimmed" aria-hidden="true"></span>
<!--v-if--></span>
"<div dir="ltr" class="flex flex-col min-h-0 min-w-0 divide-y divide-default" inputtrailingicon="i-lucide-settings">
<div class="relative inline-flex items-center [&amp;>input]:h-12"><input type="text" placeholder="Type a command or search..." class="w-full rounded-md border-0 placeholder:text-dimmed focus:outline-none disabled:cursor-not-allowed disabled:opacity-75 transition-colors px-2.5 py-1.5 text-sm gap-1.5 text-highlighted bg-transparent ps-9" autocomplete="off" aria-disabled="false" value="" aria-activedescendant="reka-listbox-item-v-0-0-1"><span class="absolute inset-y-0 start-0 flex items-center ps-2.5"><span class="iconify i-lucide:search shrink-0 text-dimmed size-5" aria-hidden="true"></span></span>
<!--v-if-->
</div>
<div class="relative overflow-hidden flex flex-col" role="listbox" aria-orientation="vertical" aria-multiselectable="false" data-orientation="vertical">
<div role="presentation" class="relative divide-y divide-default scroll-py-1 overflow-y-auto flex-1 focus:outline-none">