Composables

useShortcuts

Composable singleton per registrare scorciatoie da tastiera con cleanup automatico per scope.

Panoramica

useShortcuts gestisce la registrazione e il binding delle scorciatoie da tastiera. Lo state è condiviso tra tutte le istanze (pattern singleton): qualsiasi componente che lo invoca accede allo stesso registro globale. Il listener keydown su window viene installato una sola volta, lazily, alla prima chiamata. Le scorciatoie passate in initialShortcuts vengono rimosse automaticamente al dispose dello scope corrente (onUnmounted).

Firma

function useShortcuts(
  initialShortcuts?: Shortcut[],
  config?: ShortcutConfig
): UseShortcutsReturn

Parametri

ParametroTipoDefaultDescrizione
initialShortcutsShortcut[][]Scorciatoie da registrare per lo scope corrente. Vengono rimosse al dispose
configShortcutConfig—Configurazione globale. Viene fusa nello state globale ad ogni chiamata che la passa

ShortcutConfig

CampoTipoDefaultDescrizione
preventDefaultbooleantrueChiama event.preventDefault() quando una scorciatoia matcha
stopPropagationbooleantrueChiama event.stopPropagation() quando una scorciatoia matcha
enabledboolean | Ref<boolean>trueAbilitazione globale del sistema. Un Ref viene letto a ogni tasto
macOSMapping'auto' | 'native' | (event, shortcut) => boolean'auto'Matching su macOS: 'auto' rimappa alt → Ctrl e ctrl → Cmd, 'native' confronta i tasti così come sono, una funzione decide da sé

La configurazione vale anche per il Ctrl+K della command palette, che è una scorciatoia di questo registro.

Shortcut

interface Shortcut {
  id: string
  key: string
  ctrl?: boolean
  alt?: boolean
  shift?: boolean
  meta?: boolean
  description: string
  handler: () => void | Promise<void>
  enabled?: boolean
  allowInInput?: boolean
}

Vedi Registrazione scorciatoie per i dettagli sui singoli campi.

Return

ProprietàTipoDescrizione
shortcutsReadonly<Ref<Shortcut[]>>Registro globale corrente delle scorciatoie
addShortcut(shortcut: Shortcut) => voidAggiunge una scorciatoia. Se l'id esiste già, l'invocazione è ignorata
removeShortcut(id: string) => voidRimuove la scorciatoia con l'id specificato

Il return type è UseShortcutsReturn.

Esempio base

<script setup lang="ts">
import { useShortcuts } from '@pzeta/vue-scorciatoie'

useShortcuts([
  {
    id: 'salva',
    key: 's',
    ctrl: true,
    description: 'Salva',
    handler: () => salva(),
    allowInInput: true,
  },
  {
    id: 'annulla',
    key: 'Escape',
    description: 'Annulla',
    handler: () => annulla(),
  },
])
</script>

Esempio dinamico

const { addShortcut, removeShortcut, shortcuts } = useShortcuts()

addShortcut({
  id: 'rinomina',
  key: 'F2',
  description: 'Rinomina elemento',
  handler: avviaRinomina,
})

// Lista corrente (readonly, non mutare)
console.log(`${shortcuts.value.length} scorciatoie registrate`)

removeShortcut('rinomina')

Esempio con disabilitazione reattiva

import { ref } from 'vue'
import { useShortcuts } from '@pzeta/vue-scorciatoie'

const shortcutsAttive = ref(true)

useShortcuts(
  [
    {
      id: 'nuovo',
      key: 'n',
      alt: true,
      description: 'Nuovo elemento',
      handler: creaNuovo,
    },
  ],
  {
    enabled: shortcutsAttive, // Disabilita tutto quando false
  }
)

// Disabilita temporaneamente (es. apertura di una modale)
shortcutsAttive.value = false

Logica di matching

L'evento keydown viene confrontato con ogni scorciatoia registrata; il primo match esegue l'handler e termina il loop. Il matching:

  1. Confronta event.key (case-insensitive) con shortcut.key
  2. Verifica i modificatori secondo la piattaforma (un modificatore non definito non deve essere premuto):
    • Windows/Linux: matching diretto di ctrl, alt, meta, shift
    • macOS con macOSMapping: 'auto' (BR-17/BR-18): alt definito → richiede event.ctrlKey; ctrl definito → richiede event.metaKey. event.altKey (Option fisico) deve essere non premuto, e meta non è supportato
  3. Salta la scorciatoia se il focus è in un elemento editabile (anche dentro un antenato [contenteditable] o [role="textbox"]) e allowInInput è false
  4. Salta se enabled === false sulla scorciatoia o nel config globale

Al match vengono applicati preventDefault e stopPropagation secondo la configurazione. Gli handler asincroni (Promise) sono supportati: un errore, sincrono o asincrono, non interrompe il listener e viene loggato in console solo in sviluppo.

Cleanup automatico

Le scorciatoie passate in initialShortcuts sono tracciate e rimosse automaticamente quando lo scope effect del composable viene disposto (componente smontato). Le scorciatoie aggiunte successivamente con addShortcut sullo stesso scope subiscono lo stesso cleanup; quelle aggiunte da scope diversi richiedono removeShortcut esplicito.

Test

Lo state è a livello di modulo e la funzione di reset usata dai test della libreria non è esportata dal pacchetto. Nei test di un'applicazione si ottiene un registro pulito smontando i componenti che hanno registrato le scorciatoie (vengono rimosse al dispose dello scope).

Note

  • Il listener keydown è installato su window con bubbling standard. Eventi catturati in fase di capture da altri listener possono prevenire il match
  • key è la proprietà DOM standard KeyboardEvent.key: usare 'Enter', 'ArrowUp', 'Escape', ' ' (spazio), ecc.
  • Per visualizzare la combinazione corretta in UI usa useOS e le sue label modificatore