Skróty klawiszowe

View on GitHub

Skróty klawiszowe

Każdy skrót klawiszowy w Orbicie — ? po pomoc, mod+k, alt+p, scope'owane do strony p po "utwórz projekt" — przechodzi przez jeden system: ShortcutContext. Nie ma żadnego per-komponentowego listenera keydown nigdzie w aplikacji; jeden globalny listener rozstrzyga każde naciśnięcie klawisza względem zbudowanego w runtime rejestru, a modal pomocy (?) jest generowany w całości z tego samego rejestru — nie ma osobnej, ręcznie utrzymywanej listy skrótów do utrzymywania w zgodzie.

Przewodniki, w kolejności, w jakiej faktycznie będziesz ich potrzebować

  1. Zarejestruj skrót zakresu komponentuSkróty klawiszoweZarejestruj skrót zakresu komponentuPrzećwiczony przykład: dodanie skrótu n na stronie listy projektów, który otwiera przepływ "nowy projekt" — scope'owany do tej strony, w przeciwieństwie do… — przećwiczony przykład dodania skrótu, który ma sens tylko, gdy konkretna strona/komponent jest zamontowany, używając hooka useShortcuts() w ten sam sposób, w jaki już robi to p ("Create project") w Sidebar.tsx.
  2. Zarejestruj skrót globalnySkróty klawiszoweZarejestruj skrót globalnyPrzećwiczony przykład: dodanie alt+i dla "Go to my Issues" (strona, która jeszcze nie ma trasy, użyta czysto do zademonstrowania kształtu rejestracji) — sk… — przećwiczony przykład dodania skrótu, który powinien istnieć wszędzie w aplikacji, zadeklarowanego jako globalna komenda ze shortcut w ten sam sposób, w jaki już są alt+p/alt+b/?.

Architektura w jednym akapicie

ShortcutProvider (resources/js/context/ShortcutContext.tsx) trzyma jedną tablicę ShortcutDefinition (key, description, category, action, opcjonalny disabled) w stanie — nic nie jest zakodowane na sztywno w samym handlerze keydown. register()/registerBatch() dodają do tej tablicy i zwracają funkcję wyrejestrowania, mającą być wywołaną z czyszczenia useEffect (albo, częściej, przez hook useShortcuts(definitions), który robi to dokładnie za Ciebie — przekaż tablicę, dostań z powrotem context, a rejestracja/czyszczenie jest obsłużone przy montowaniu/odmontowaniu i za każdym razem, gdy zmienia się referencja tablicy). Pojedynczy window.addEventListener('keydown', ..., true) (faza capture, więc uruchamia się zanim zafokusowany input mógłby połknąć event) buduje string pressedKey z flag modyfikatorów eventu (prefiksy ctrl+/alt+/meta+/shift+) i osobno śledzi "combo" ostatnich naciśnięć klawiszy bez modyfikatora w oknie 500ms (dla wieloklawiszowych sekwencji jak g d czy g p, które rejestrują globalne komendy nawigacji — wygrywa najdłuższy ciąg ostatnich klawiszy, więc g p bije jednoklawiszowe p), a potem szuka ShortcutDefinition, którego key pasuje do którejkolwiek formy. Skróty są całkowicie ignorowane, gdy fokus ma <input>/<textarea>/element contenteditable, poza Escape. ShortcutProvider sam niczego nie rejestruje: skróty obejmujące całą aplikację są atrybutami komend (CommandContext, useGlobalCommands), które przekazują swój shortcut do registerBatch(), dopóki są zarejestrowane. Modal pomocy (ShortcutHelpModal, otwierany przez ?, referencja tylko do odczytu) odczytuje shortcuts z tego samego contextu i grupuje/wyszukuje cokolwiek jest aktualnie zarejestrowane — nie ma żadnej wiedzy o żadnym skrócie poza tym, co jest żywe w tablicy w momencie, gdy się renderuje.