CommandPalette
Panoramica
CommandPalette è il componente UI della command palette. Si apre automaticamente con Ctrl+K (Windows/Linux) o Cmd+K (macOS) e mostra l'elenco dei comandi registrati tramite useCommandPalette. Supporta ricerca testuale incrementale, navigazione con frecce, esecuzione con Enter, chiusura con Esc o click sull'overlay.
Il componente usa <Teleport to="body"> ed è quindi inseribile in qualsiasi punto del template senza vincoli di posizionamento.
Anteprima live
Click sul pulsante per aprire la palette, oppure premi Ctrl+K / Cmd+K. Naviga con frecce, conferma con Enter, chiudi con Esc.
Props
CommandPalette non espone props pubbliche: lo state è interamente gestito dal singleton useCommandPalette. Per personalizzare aspetto e comportamento agisci sul composable e sulla registrazione dei comandi.
Eventi
Il componente non emette eventi Vue. L'esecuzione di un comando avviene tramite l'handler definito sul comando stesso. La chiusura avviene automaticamente al termine dell'handler (anche in caso di errore).
Slot
CommandPalette non espone slot pubblici nella versione 1.0.0. La visualizzazione di un comando combina icon, label, description e category direttamente dal tipo Command.
Layout
La struttura del componente è la seguente:
- Overlay:
fixed inset-0 z-50con sfondobg-black/50. Click sull'overlay chiude la palette - Dialog:
bg-white dark:bg-gray-900 rounded-lg shadow-2xl max-w-2xl, ARIArole="dialog"conaria-modal="true" - Input ricerca:
role="combobox"conaria-controls,aria-autocomplete="list",aria-activedescendantsincronizzato con la voce selezionata - Listbox risultati:
role="listbox"conmax-h-96 overflow-y-auto. Ogni voce èrole="option"conaria-selected - Annuncio screen reader: regione
aria-live="polite"che riporta il numero di risultati per la query corrente - Empty state: messaggio "Nessun comando trovato" quando il filtro non produce match
Il dark mode è gestito tramite la classe dark: di Tailwind.
Interazioni tastiera
| Tasto | Azione |
|---|---|
Ctrl+K / Cmd+K | Apre/chiude la palette (gestito da useCommandPalette) |
ArrowDown | Selezione successiva |
ArrowUp | Selezione precedente |
Enter | Esegue il comando selezionato |
Escape | Chiude la palette |
Tab / Shift+Tab | Focus trap interno alla modale |
L'apertura sposta il focus sull'input di ricerca e azzera l'indice selezionato a 0. Alla chiusura il focus viene riportato sull'elemento che lo possedeva prima dell'apertura, garantendo continuità di navigazione tastiera.
Esempio base
<script setup lang="ts">
import { CommandPalette, useCommandPalette } from '@pzeta/vue-scorciatoie'
useCommandPalette([
{
id: 'apri-anagrafica',
label: 'Apri anagrafica',
handler: () => router.push('/anagrafica'),
},
{
id: 'nuova-attivita',
label: 'Nuova attività',
handler: () => apriDialogAttivita(),
},
])
</script>
<template>
<CommandPalette />
<RouterView />
</template>
Esempio con categorie e icone
<script setup lang="ts">
import { CommandPalette, useCommandPalette } from '@pzeta/vue-scorciatoie'
useCommandPalette([
{
id: 'nav-dashboard',
label: 'Dashboard',
description: 'Vai alla dashboard principale',
category: 'Navigazione',
icon: 'pi pi-home',
keywords: ['home', 'inizio'],
handler: () => router.push('/'),
},
{
id: 'nav-anagrafica',
label: 'Anagrafiche',
description: 'Elenco clienti e fornitori',
category: 'Navigazione',
icon: 'pi pi-users',
keywords: ['clienti', 'fornitori'],
handler: () => router.push('/anagrafica'),
},
{
id: 'azione-nuovo-ticket',
label: 'Nuovo ticket',
description: 'Apre il form per creare un ticket',
category: 'Azioni',
icon: 'pi pi-plus',
keywords: ['support', 'crea'],
handler: () => apriFormTicket(),
},
{
id: 'azione-logout',
label: 'Logout',
category: 'Sistema',
icon: 'pi pi-sign-out',
handler: () => authStore.logout(),
},
])
</script>
<template>
<CommandPalette />
</template>
Le keyword non sono visualizzate ma partecipano al filtro testuale: digitare "support" trova "Nuovo ticket" anche se la parola non compare nel label.
Esempio con comandi dinamici
I comandi possono essere registrati e rimossi a runtime. Usa addCommand/removeCommand per registrazioni che dipendono dal contesto (es. moduli caricati on-demand, permessi utente):
import { useCommandPalette } from '@pzeta/vue-scorciatoie'
const { addCommand, removeCommand } = useCommandPalette()
watch(modulePermissions, (perms) => {
if (perms.includes('admin.users')) {
addCommand({
id: 'admin-users',
label: 'Gestione utenti',
category: 'Admin',
handler: () => router.push('/admin/users'),
})
} else {
removeCommand('admin-users')
}
})
Note
- I comandi con
enabled: falsevengono filtrati e non compaiono nei risultati - L'esecuzione dell'handler avviene in
try/catch: gli errori vengono loggati conconsole.errorma la palette si chiude comunque - Il componente è completamente accessibile (WCAG 2.1) con focus trap, annuncio risultati e gestione
aria-activedescendant - Lo styling è basato su Tailwind con dark mode automatico via classi
dark: