Registrazione scorciatoie
Panoramica
Le scorciatoie si registrano tramite il composable useShortcuts, che mantiene un registro globale singleton. Una scorciatoia è identificata da id univoco e può combinare un tasto principale con i modificatori ctrl, alt, shift, meta.
Definizione di una scorciatoia
import type { Shortcut } from '@pzeta/vue-scorciatoie'
const shortcut: Shortcut = {
id: 'salva-record',
key: 's',
ctrl: true,
description: 'Salva il record corrente',
handler: () => salvaRecord(),
enabled: true,
allowInInput: false,
}
| Campo | Tipo | Default | Descrizione |
|---|---|---|---|
id | string | — | Identificatore univoco. Una seconda registrazione con lo stesso id viene ignorata |
key | string | — | Tasto principale (case-insensitive, es. 's', 'Enter', 'ArrowUp') |
ctrl | boolean | false | Richiede Ctrl (Windows/Linux) o Cmd (macOS) |
alt | boolean | false | Richiede Alt (Windows/Linux) o Ctrl (macOS) |
shift | boolean | false | Richiede Shift |
meta | boolean | false | Richiede tasto Meta esplicito (Windows/Linux). Su macOS il matching meta è sostituito dalla logica BR-18 |
description | string | — | Descrizione testuale per cheat-sheet o tooltip |
handler | () => void | Promise<void> | — | Funzione invocata quando la scorciatoia è premuta. Errori asincroni vengono loggati con console.error |
enabled | boolean | true | Se false, la scorciatoia è ignorata senza essere rimossa |
allowInInput | boolean | false | Se false, la scorciatoia non scatta quando il focus è su <input>, <textarea>, <select> o elementi contentEditable |
Scorciatoie globali
Registrate in un componente di alto livello (es. App.vue o un layout) restano attive per l'intera vita dell'applicazione finché lo scope non viene disposto.
<script setup lang="ts">
import { useShortcuts } from '@pzeta/vue-scorciatoie'
useShortcuts([
{
id: 'apri-palette',
key: 'k',
ctrl: true,
description: 'Apri command palette',
handler: () => paletteStore.open(),
},
{
id: 'cerca-globale',
key: '/',
description: 'Focus barra di ricerca globale',
handler: () => document.getElementById('search')?.focus(),
allowInInput: false,
},
])
</script>
Scorciatoie contestuali
Registrate in un componente specifico vengono rimosse automaticamente quando il componente è smontato, grazie a onScopeDispose interno.
<script setup lang="ts">
// Solo nella vista dettaglio anagrafica
import { useShortcuts } from '@pzeta/vue-scorciatoie'
useShortcuts([
{
id: 'anagrafica-salva',
key: 's',
ctrl: true,
description: 'Salva anagrafica',
handler: handleSave,
allowInInput: true, // Anche se il focus è su un campo del form
},
{
id: 'anagrafica-elimina',
key: 'Delete',
shift: true,
description: 'Elimina anagrafica',
handler: handleDelete,
},
])
</script>
Cross-platform: BR-17 e BR-18
Le scorciatoie si definiscono sempre con la semantica Windows/Linux. Su macOS il matching adatta automaticamente:
| Definizione | Windows/Linux | macOS |
|---|---|---|
alt: true | Alt + key | Ctrl + key (BR-17) |
ctrl: true | Ctrl + key | Cmd + key (BR-18) |
Su macOS il tasto Option fisico non deve essere premuto per evitare conflitti con i caratteri speciali Option+key.
useShortcuts([
{
id: 'nuova-anagrafica',
key: 'n',
alt: true, // Alt+N su Windows/Linux, Ctrl+N su macOS
description: 'Nuova anagrafica',
handler: () => apriDialogNuova(),
},
])
Per visualizzare correttamente il modificatore nell'interfaccia, usare il composable useOS e le sue label primaryModifierLabel / paletteModifierLabel.
Configurazione globale
Il secondo parametro di useShortcuts accetta una ShortcutConfig che viene fusa nello state globale:
useShortcuts(scorciatoie, {
preventDefault: true, // Default true
stopPropagation: true, // Default true
enabled: ref(isEnabled), // Accetta boolean o Ref<boolean>
})
La configurazione è applicata a tutte le scorciatoie registrate. enabled accetta un Ref<boolean> per disabilitare temporaneamente l'intero sistema (es. quando una modale ha il focus).
Aggiungere e rimuovere dinamicamente
useShortcuts espone API imperativa per registrazioni dinamiche:
const { addShortcut, removeShortcut, shortcuts } = useShortcuts()
addShortcut({
id: 'temporanea',
key: 'F2',
description: 'Rinomina',
handler: rinomina,
})
// In un secondo momento
removeShortcut('temporanea')
// Lista corrente (readonly)
console.log(shortcuts.value.length)
Priorità ed ordine di matching
Quando un evento keydown corrisponde a più scorciatoie, viene invocato l'handler della prima trovata nell'array (ordine di registrazione). Il loop si interrompe al primo match: usa l'ordine di registrazione come priorità implicita.
Comportamento in input
Per default le scorciatoie sono ignorate quando il focus è su un elemento editabile (input, textarea, select, [contenteditable]). Imposta allowInInput: true per permettere il match anche durante la digitazione (es. salvataggio con Ctrl+S mentre si compila un form).
Esempi pratici
Salva con Ctrl+S, anche durante la digitazione
useShortcuts([
{
id: 'salva',
key: 's',
ctrl: true,
allowInInput: true,
description: 'Salva',
handler: salvaForm,
},
])
Annulla con Esc fuori dagli input
useShortcuts([
{
id: 'annulla',
key: 'Escape',
description: 'Chiudi pannello',
handler: () => panelStore.close(),
},
])
Disabilitazione condizionale
const { addShortcut, removeShortcut } = useShortcuts()
watch(modalAperta, (aperta) => {
if (aperta) {
addShortcut({
id: 'modal-chiudi',
key: 'Escape',
description: 'Chiudi modal',
handler: chiudiModal,
})
} else {
removeShortcut('modal-chiudi')
}
})