This commit is contained in:
HugoRCD
2025-07-07 11:51:34 +02:00
parent b7ab65b0c9
commit 259a43930c
2 changed files with 73 additions and 34 deletions

View File

@@ -10,8 +10,9 @@ const open = ref(false)
const searchTerm = ref('') const searchTerm = ref('')
// const searchTermDebounced = refDebounced(searchTerm, 200) // const searchTermDebounced = refDebounced(searchTerm, 200)
const selected = ref([]) const selected = ref([])
const commandPalette = useTemplateRef('commandPalette')
const { data: users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', { const { data: _users, status } = await useFetch('https://jsonplaceholder.typicode.com/users', {
// params: { q: searchTermDebounced }, // params: { q: searchTermDebounced },
transform: (data: User[]) => { transform: (data: User[]) => {
return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || [] return data?.map(user => ({ id: user.id, label: user.name, suffix: user.email, avatar: { src: `https://i.pravatar.cc/120?img=${user.id}` } })) || []
@@ -259,6 +260,12 @@ function onSelect(item: any) {
defineShortcuts({ defineShortcuts({
meta_k: () => open.value = !open.value, meta_k: () => open.value = !open.value,
meta_shift_a: {
usingInput: true,
handler: () => {
commandPalette.value?.openView('askAI')
}
},
...extractShortcuts(groups.value) ...extractShortcuts(groups.value)
}) })
</script> </script>
@@ -266,6 +273,7 @@ defineShortcuts({
<template> <template>
<DefineTemplate> <DefineTemplate>
<UCommandPalette <UCommandPalette
ref="commandPalette"
v-model="selected" v-model="selected"
v-model:search-term="searchTerm" v-model:search-term="searchTerm"
:loading="status === 'pending'" :loading="status === 'pending'"
@@ -279,8 +287,7 @@ defineShortcuts({
class="sm:max-h-80" class="sm:max-h-80"
@update:model-value="onSelect" @update:model-value="onSelect"
> >
<template #view="{ viewName }"> <template #wallpaper>
<div v-if="viewName === 'wallpaper'" class="flex flex-col h-full w-full">
<div class="flex-1 overflow-y-auto p-6"> <div class="flex-1 overflow-y-auto p-6">
<div class="grid grid-cols-4 gap-4"> <div class="grid grid-cols-4 gap-4">
<div <div
@@ -313,6 +320,14 @@ defineShortcuts({
</div> </div>
</div> </div>
</div> </div>
</template>
<template #askAI>
<div class="flex flex-col items-center justify-center gap-4 p-6">
<UIcon name="i-lucide-sparkles" class="size-8 text-primary" />
<span class="text-lg font-semibold text-highlighted">
Ask me anything...
</span>
</div> </div>
</template> </template>
</UCommandPalette> </UCommandPalette>

View File

@@ -154,12 +154,11 @@ export type CommandPaletteSlots<G extends CommandPaletteGroup<T> = CommandPalett
'empty'(props: { searchTerm?: string }): any 'empty'(props: { searchTerm?: string }): any
'back'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any 'back'(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 'close'(props: { ui: { [K in keyof Required<CommandPalette['slots']>]: (props?: Record<string, any>) => string } }): any
'view'(props: { viewName?: string, current: any, searchTerm: string, navigateBack: () => void }): any
'item': SlotProps<T> 'item': SlotProps<T>
'item-leading': SlotProps<T> 'item-leading': SlotProps<T>
'item-label': SlotProps<T> 'item-label': SlotProps<T>
'item-trailing': SlotProps<T> 'item-trailing': SlotProps<T>
} & Record<string, SlotProps<G>> & Record<string, SlotProps<T>> } & Record<string, SlotProps<G>> & Record<string, SlotProps<T>> & Record<string, (props: { current: any, searchTerm: string, navigateBack: () => void, close: () => void }) => any>
</script> </script>
@@ -290,6 +289,31 @@ const filteredGroups = computed(() => {
const listboxRootRef = useTemplateRef('listboxRootRef') const listboxRootRef = useTemplateRef('listboxRootRef')
// Exposed methods for programmatic control
function openView(viewName: string) {
history.value.push({
id: `view-${viewName}`,
label: viewName,
view: viewName,
items: []
} as any)
searchTerm.value = ''
listboxRootRef.value?.highlightFirstItem()
}
function closeView() {
if (history.value.length > 0) {
navigateBack()
}
}
defineExpose({
openView,
closeView,
navigateBack
})
function navigate(item: T) { function navigate(item: T) {
if (!item.children?.length && !item.view) { if (!item.children?.length && !item.view) {
return return
@@ -385,11 +409,11 @@ function onSelect(e: Event, item: T) {
<ListboxContent :class="ui.content({ class: props.ui?.content })"> <ListboxContent :class="ui.content({ class: props.ui?.content })">
<div v-if="currentView" :class="ui.viewport({ class: props.ui?.viewport })"> <div v-if="currentView" :class="ui.viewport({ class: props.ui?.viewport })">
<slot <slot
name="view" :name="currentView.view"
:view-name="currentView.view"
:current="currentView" :current="currentView"
:search-term="searchTerm" :search-term="searchTerm"
:navigate-back="navigateBack" :navigate-back="navigateBack"
:close="closeView"
/> />
</div> </div>