HextaUI

useHeldKeys

Die Tasten, die gerade gehalten werden, geteilt von allen Abonnenten über einen einzigen Satz Window-Listener.

Hold down any keys
[]
"use client"

import { Kbd, KbdGroup } from "@/components/ui/kbd"
import { useHeldKeys } from "@/hooks/use-held-keys"

export function UseHeldKeysDemo() {
  const held = useHeldKeys(true)

  return (
    <div className="flex flex-col items-center gap-3">
      <div className="flex h-8 items-center">
        {held.size > 0 ? (
          <KbdGroup>
            {[...held].map((key) => (
              <Kbd key={key} keys={key} size="lg" />
            ))}
          </KbdGroup>
        ) : (
          <span className="text-sm text-muted-foreground">
            Hold down any keys
          </span>
        )}
      </div>
      <code className="font-mono text-xs text-muted-foreground">
        {JSON.stringify([...held])}
      </code>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/use-held-keys.json

Fügt den Hook und alles, wovon er abhängt, zu deinem Projekt hinzu.

import { useHeldKeys } from "@/hooks/use-held-keys"
const held = useHeldKeys(true)
const showShortcutHints = held.has("meta") || held.has("ctrl")

Nutze ihn für alles, was auf gehaltene statt gedrückte Tasten reagiert: Tasten, die sich eindrücken, Tastenkürzel-Hinweise, die erscheinen, solange du ⌘ hältst, oder einen Modifier, der ein Werkzeug umschaltet, wie Alt zum Duplizieren beim Ziehen.

Jede Komponente, die den Hook aufruft, teilt sich einen Store. Der erste Abonnent fügt passive keydown- und keyup-Listener an window hinzu, und der letzte, der sich abmeldet, entfernt sie. Eine Seite mit fünfzig lauschenden Tasten hat weiterhin ein Paar Listener.

Die Listener lesen Events nur. Sie rufen nie preventDefault auf und sitzen an window, nach den eigenen Handlern von React, sodass das Tippen in einem Feld nie verzögert oder verändert wird.

"meta" "ctrl" "alt" "shift"     modifiers, left and right alike
"a" … "z"  "0" … "9"            letters and digits, by physical key
"space" "enter" "escape" "tab"   named keys, lowercased
"arrowup" "arrowdown" "f1" …
  • Buchstaben und Ziffern stammen aus event.code, der physischen Taste, sodass das Halten von Option+K auf einem Mac weiterhin k meldet statt ˚.
  • Tastenwiederholung wird ignoriert, und nichts rendert neu, solange eine Taste gehalten wird.
  • macOS sendet kein keyup für andere Tasten, solange ⌘ gedrückt ist. Wird ⌘ losgelassen, behält der Store nur die noch gehaltenen Modifier, sodass Buchstaben nicht hängen bleiben können.
  • Alles wird losgelassen, wenn das Fenster den Fokus verliert oder der Tab verborgen wird. Ein Tastenkürzel, das die App wechselt, lässt nichts gehalten zurück.

Tasten, die sich drücken

<Kbd listen> ist auf diesem Hook gebaut. Jede Taste drückt sich nach unten, solange ihre Taste gehalten wird.

"use client"

import { Kbd, KbdGroup } from "@/components/ui/kbd"
import { useHeldKeys } from "@/hooks/use-held-keys"
import { useIsApple } from "@/lib/hotkey"

export function UseHeldKeysShortcut() {
  const held = useHeldKeys(true)
  const apple = useIsApple()
  const modifier = apple ? "meta" : "ctrl"
  const ready = held.has(modifier) && held.has("shift")

  return (
    <div className="flex flex-col items-center gap-3 text-sm">
      <KbdGroup>
        <Kbd keys="mod" listen />
        <Kbd keys="shift" listen />
        <Kbd keys="p" listen />
      </KbdGroup>
      <p className="text-muted-foreground">
        {ready ? "Now press P" : "Hold the modifiers to see the hint"}
      </p>
    </div>
  )
}
  • Übergib false, um das Lauschen zu beenden. Der Hook gibt dann eine leere Menge zurück und fügt keine Listener hinzu, sodass er bedenkenlos bedingungslos aufgerufen werden kann.
  • Die Menge wird nur ersetzt, wenn eine Taste gedrückt oder losgelassen wird, sodass ihre Identität als Memo- oder Effect-Abhängigkeit taugt.
  • Für Tastenkürzel, die eine Aktion auslösen, nutze stattdessen matchesHotkey aus Hotkey in einem keydown-Handler. Das Halten von Tasten dient der Zustandsanzeige, nicht dem Ausführen von Befehlen.
  • Auf dem Server und vor der Hydration ist die Menge leer.
PropTypStandard
enabledOb gelauscht wird. Bei false wird nichts angehängt.
boolean–
RückgabeBeschreibung
ReadonlySet<string>Die Namen der Tasten, die gerade gehalten werden.

Kbd und KbdGroup über ihre listen-Prop.