HextaUI

Hotkey

Klavye kısayollarını ayrıştırır, etiketler, duyurur ve eşleştirir; Apple platformlarında ⌘, diğer her yerde Ctrl kullanır.

Apple
⇧⌘K
Windows and Linux
Shift+Ctrl+K
Screen readers
Shift Command K
parseHotkey
[["shift","mod","k"]]

Click outside the field and press Shift Command Kmatched 0×

"use client"

import * as React from "react"

import { Input } from "@/components/ui/input"
import { Kbd } from "@/components/ui/kbd"
import {
  formatHotkey,
  matchesHotkey,
  parseHotkey,
  spokenKey,
  useIsApple,
} from "@/lib/hotkey"

export function HotkeyDemo() {
  const [hotkey, setHotkey] = React.useState("mod+shift+k")
  const [pressed, setPressed] = React.useState(0)
  const apple = useIsApple()
  const chord = parseHotkey(hotkey)[0] ?? []

  React.useEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      if (
        event.target instanceof HTMLInputElement ||
        !matchesHotkey(event, hotkey)
      ) {
        return
      }
      event.preventDefault()
      setPressed((count) => count + 1)
    }
    window.addEventListener("keydown", onKeyDown)
    return () => window.removeEventListener("keydown", onKeyDown)
  }, [hotkey])

  const rows = [
    ["Apple", formatHotkey(hotkey, true)],
    ["Windows and Linux", formatHotkey(hotkey, false)],
    ["Screen readers", chord.map((key) => spokenKey(key, apple)).join(" ")],
    ["parseHotkey", JSON.stringify(parseHotkey(hotkey))],
  ]

  return (
    <div className="flex w-full max-w-sm flex-col gap-4">
      <Input
        aria-label="Hotkey"
        value={hotkey}
        onChange={(event) => setHotkey(event.target.value)}
        spellCheck={false}
        autoCapitalize="off"
      />
      <dl className="grid grid-cols-[auto_minmax(0,1fr)] gap-x-6 gap-y-2 text-sm">
        {rows.map(([label, value]) => (
          <React.Fragment key={label}>
            <dt className="text-muted-foreground">{label}</dt>
            <dd className="truncate font-mono">{value}</dd>
          </React.Fragment>
        ))}
      </dl>
      <p className="flex items-center gap-2 text-sm text-muted-foreground">
        Click outside the field and press <Kbd keys={hotkey} />
        <span className="ms-auto font-mono tabular-nums">
          matched {pressed}×
        </span>
      </p>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/hotkey.json

Yardımcıyı (utility) ve bağımlı olduğu her şeyi projenize ekler.

import {
  formatHotkey,
  matchesHotkey,
  parseHotkey,
  useIsApple,
} from "@/lib/hotkey"
function SaveShortcut({ onSave }: { onSave: () => void }) {
  const apple = useIsApple()

  React.useEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      if (matchesHotkey(event, "mod+s")) {
        event.preventDefault()
        onSave()
      }
    }
    window.addEventListener("keydown", onKeyDown)
    return () => window.removeEventListener("keydown", onKeyDown)
  }, [onSave])

  return <span>Save {formatHotkey("mod+s", apple)}</span>
}

Her kısayolu bir dize olarak bir kez yazın ve aynı dizeyi göstermek, duyurmak ve eşleştirmek için kullanın. mod, Apple platformlarında ⌘, diğer her yerde Ctrl anlamına gelir. Neredeyse her zaman istediğiniz budur, çünkü Mac'te Ctrl+K ve Windows'ta ⌘K ikisi de yanlış hissettirir.

"mod+k"            ⌘K on Apple platforms, Ctrl+K elsewhere
"shift+mod+p"      modifiers in any order
"cmd+option+esc"   aliases: cmd, command, option, opt, control, esc, return
"alt+up"           up, down, left and right for the arrow keys
"mod++"            a literal plus key
"g g"              a sequence of two chords, separated by a space

Tuşlar + ile ayrılır ve büyük/küçük harfe duyarsızdır. Adlandırılmış tuşlar enter, tab, pageup veya f5 gibi küçük harfli KeyboardEvent.key değerini kullanır. Boşluk tuşu için space kullanın.

parseHotkey("mod+shift+k")  // [["shift", "mod", "k"]]
parseHotkey("cmd+opt+esc")  // [["alt", "meta", "escape"]]
parseHotkey("g i")          // [["g"], ["i"]]

parseHotkey her tuş kombinasyonu için bir dizi döndürür; takma adlar çözümlenir ve değiştiriciler Apple'ın sırasına göre dizilir: Control, Option, Shift, Command. Ondan oluşturulan her etiket, insanların görmeyi beklediği sırada okunur.

formatHotkey("mod+shift+k", true)   // "⇧⌘K"
formatHotkey("mod+shift+k", false)  // "Shift+Ctrl+K"
keyLabel("enter", true)             // "↵"
spokenKey("mod", true)              // "Command"
spokenKey("mod", false)             // "Control"

Apple platformları menülerde gösterildiği gibi ayırıcısız semboller kullanır. Windows ve Linux + ile birleştirilmiş kelimeler kullanır. Semboller sesli okunmaya uygun değildir; bu yüzden spokenKey bunun yerine ekran okuyucunun duyurması gereken adı verir. <Kbd keys> sembolü gösterir ve söylenen adı görsel olarak gizli metne koyar.

useIsApple() platformu seçer. Sunucuda ve hydration sırasında true, sonra gerçek yanıtı döndürür; böylece bir Windows ziyaretçisi hydration hatası almak yerine Ctrl'den önce kısa süre ⌘ görür.

Kısayol dinleyicisi

matchesHotkey bir keydown olayını bir kısayol tuşuna göre denetler. Değiştiriciler tam eşleşmelidir; bu yüzden mod+b, mod+shift+b için tetiklenmez.

"use client"

import * as React from "react"

import { Kbd } from "@/components/ui/kbd"
import { matchesHotkey } from "@/lib/hotkey"

const shortcuts = [
  { hotkey: "mod+b", label: "Bold" },
  { hotkey: "mod+i", label: "Italic" },
  { hotkey: "mod+shift+x", label: "Strikethrough" },
]

export function HotkeyListener() {
  const [last, setLast] = React.useState<string>()

  React.useEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      const match = shortcuts.find((shortcut) =>
        matchesHotkey(event, shortcut.hotkey)
      )
      if (match) {
        event.preventDefault()
        setLast(match.label)
      }
    }
    window.addEventListener("keydown", onKeyDown)
    return () => window.removeEventListener("keydown", onKeyDown)
  }, [])

  return (
    <div className="flex w-full max-w-xs flex-col gap-3">
      <ul className="flex flex-col gap-2 text-sm">
        {shortcuts.map((shortcut) => (
          <li
            key={shortcut.hotkey}
            data-active={last === shortcut.label ? "" : undefined}
            className="flex items-center justify-between rounded-md px-2 py-1 transition-colors duration-150 data-active:bg-muted"
          >
            {shortcut.label}
            <Kbd keys={shortcut.hotkey} />
          </li>
        ))}
      </ul>
      <p role="status" className="text-sm text-muted-foreground">
        {last ? `Matched ${last}` : "Press a shortcut"}
      </p>
    </div>
  )
}
  • Harfler ve rakamlar fiziksel tuşa göre de eşleşir; bu yüzden Option+K'nın ˚ yazdığı bir Mac'te alt+k çalışır.
  • Shift'li harfler eşleşir: shift+k, Shift'in ürettiği K ile eşleşir.
  • Tek bir tuş kombinasyonunu eşleştirir. g i gibi diziler için önceki kombinasyonu kendiniz izleyin.
  • Odak bir metin alanındayken değiştirici tuşu olmayan kısayolları atlayın; böylece bir harf yazmak asla bir komutu tetiklemez.
ExportAçıklama
parseHotkey(hotkey)string[][]: her tuş kombinasyonu için bir dizi; takma adlar çözümlenmiş ve değiştiriciler sıralanmış.
formatHotkey(hotkey, apple)⇧⌘K veya Shift+Ctrl+K gibi tek bir tuş kombinasyonunun etiketi.
keyLabel(key, apple)Bir tuş adının görünür etiketi.
spokenKey(key, apple)Bir ekran okuyucunun tek bir tuş için duyurması gereken ad.
matchesHotkey(event, hotkey)Bir KeyboardEvent'in, tam değiştiricilerle tek bir tuş kombinasyonuyla eşleşip eşleşmediği.
isApplePlatform()navigator.platform okur. Sunucuda true.
useIsApple()isApplePlatform, hydration açısından güvenli bir hook olarak.

Kbd ve Command.