diff --git a/playground/app/pages/components/command-palette.vue b/playground/app/pages/components/command-palette.vue index 5dd4de269f..63266921d4 100644 --- a/playground/app/pages/components/command-palette.vue +++ b/playground/app/pages/components/command-palette.vue @@ -10,8 +10,9 @@ const open = ref(false) const searchTerm = ref('') // const searchTermDebounced = refDebounced(searchTerm, 200) 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 }, 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}` } })) || [] @@ -22,10 +23,6 @@ const { data: users, status } = await useFetch('https://jsonplaceholder.typicode const loading = ref(false) const groups = computed(() => [{ - id: 'users', - label: searchTerm.value ? `Users matching “${searchTerm.value}”...` : 'Users', - items: users.value || [] -}, { id: 'actions', items: [{ label: 'Add new file', @@ -74,6 +71,12 @@ const groups = computed(() => [{ toast.add({ title: 'Label added!' }) }, kbds: ['meta', 'L'] + }, { + label: 'Set Wallpaper', + suffix: 'Choose from beautiful wallpaper collection.', + icon: 'i-lucide-image', + view: 'wallpaper', + placeholder: 'Search wallpapers...' }, { label: 'More actions', placeholder: 'Search actions...', @@ -140,6 +143,116 @@ const labels = [{ }] const label = ref() +const wallpapers = [ + { + id: 1, + name: 'red_distortion_1', + gradient: 'from-red-500 via-orange-500 to-pink-500', + category: 'Abstract', + featured: true + }, + { + id: 2, + name: 'blue_distortion_1', + gradient: 'from-blue-600 via-purple-600 to-indigo-600', + category: 'Abstract', + featured: true + }, + { + id: 3, + name: 'mono_dark_distortion_1', + gradient: 'from-gray-900 via-gray-700 to-gray-800', + category: 'Monochrome', + featured: false + }, + { + id: 4, + name: 'chromatic_dark_1', + gradient: 'from-emerald-600 via-teal-600 to-cyan-600', + category: 'Chromatic', + featured: true + }, + { + id: 5, + name: 'red_distortion_2', + gradient: 'from-rose-600 via-red-600 to-orange-600', + category: 'Abstract', + featured: false + }, + { + id: 6, + name: 'purple_cosmic_1', + gradient: 'from-violet-700 via-purple-700 to-fuchsia-700', + category: 'Cosmic', + featured: true + }, + { + id: 7, + name: 'golden_sunset_1', + gradient: 'from-yellow-500 via-orange-500 to-red-500', + category: 'Nature', + featured: false + }, + { + id: 8, + name: 'ocean_deep_1', + gradient: 'from-blue-800 via-blue-900 to-indigo-900', + category: 'Nature', + featured: true + }, + { + id: 9, + name: 'mono_light_distortion_1', + gradient: 'from-gray-200 via-gray-300 to-gray-400', + category: 'Monochrome', + featured: false + }, + { + id: 10, + name: 'green_matrix_1', + gradient: 'from-green-800 via-emerald-700 to-teal-700', + category: 'Chromatic', + featured: false + }, + { + id: 11, + name: 'pink_dreams_1', + gradient: 'from-pink-500 via-rose-500 to-purple-500', + category: 'Abstract', + featured: true + }, + { + id: 12, + name: 'midnight_blue_1', + gradient: 'from-slate-900 via-blue-900 to-indigo-900', + category: 'Nature', + featured: false + } +] + +const filteredWallpapers = computed(() => { + let filtered = wallpapers + + // Filter by search term + if (searchTerm.value.trim()) { + const search = searchTerm.value.toLowerCase() + filtered = filtered.filter(w => + w.name.toLowerCase().includes(search) + || w.category.toLowerCase().includes(search) + ) + } + + return filtered +}) + +function setWallpaper(wallpaper: any) { + toast.add({ + title: `Wallpaper set to ${wallpaper.name}!`, + description: 'Your desktop wallpaper has been updated.', + icon: 'i-lucide-image' + }) +} + // function onSelect(item: typeof groups.value[number]['items'][number]) { function onSelect(item: any) { console.log('Selected', item) @@ -147,6 +260,12 @@ function onSelect(item: any) { defineShortcuts({ meta_k: () => open.value = !open.value, + meta_shift_a: { + usingInput: true, + handler: () => { + commandPalette.value?.openView('askAI') + } + }, ...extractShortcuts(groups.value) }) @@ -154,6 +273,7 @@ defineShortcuts({ + + + + diff --git a/src/runtime/components/CommandPalette.vue b/src/runtime/components/CommandPalette.vue index 73c7a23c09..354fcae7f9 100644 --- a/src/runtime/components/CommandPalette.vue +++ b/src/runtime/components/CommandPalette.vue @@ -31,6 +31,11 @@ export interface CommandPaletteItem extends Omit @@ -154,7 +159,7 @@ export type CommandPaletteSlots = CommandPalett 'item-leading': SlotProps 'item-label': SlotProps 'item-trailing': SlotProps -} & Record> & Record> +} & Record> & Record> & Record void, close: () => void }) => any> @@ -209,12 +214,17 @@ const fuse = computed(() => defu({}, props.fuse, { matchAllWhenSearchEmpty: true })) -const history = ref<(CommandPaletteGroup & { placeholder?: string })[]>([]) +const history = ref<(CommandPaletteGroup & { placeholder?: string, view?: string })[]>([]) const placeholder = computed(() => history.value[history.value.length - 1]?.placeholder || props.placeholder || t('commandPalette.placeholder')) const groups = computed(() => history.value?.length ? [history.value[history.value.length - 1] as G] : props.groups) +const currentView = computed(() => { + const current = history.value[history.value.length - 1] + return current?.view ? current : null +}) + const items = computed(() => groups.value?.filter((group) => { if (!group.id) { console.warn(`[@nuxt/ui] CommandPalette group is missing an \`id\` property`) @@ -280,8 +290,33 @@ const filteredGroups = computed(() => { 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) { - if (!item.children?.length) { + if (!item.children?.length && !item.view) { return } @@ -290,7 +325,8 @@ function navigate(item: T) { label: item.label, slot: item.slot, placeholder: item.placeholder, - items: item.children + view: item.view, + items: item.children || [] } as any) searchTerm.value = '' @@ -317,7 +353,7 @@ function onBackspace() { } function onSelect(e: Event, item: T) { - if (item.children?.length) { + if (item.children?.length || item.view) { e.preventDefault() navigate(item) @@ -372,7 +408,17 @@ function onSelect(e: Event, item: T) { -
+
+ +
+ +
{{ get(group, props.labelKey as string) }} @@ -416,7 +462,7 @@ function onSelect(e: Event, item: T) {