- Ruby / Rails以外の開発一般
- Ruby / Rails関連
Railsの技: StimulusJSで使えるキーボードショートカットのライブラリ(翻訳)
Railsの技: StimulusJSで使えるキーボードショートカットのライブラリ(翻訳)
本記事は、Boring Railsの新シーズンコンテンツ「Hotwire Summer」の一部です。
キーボードショートカットは、Webアプリケーションのユーザーエクスペリエンスを高めて使い勝手を改善する優れた方法のひとつです。ホットキーの使いこなし方を知らない人にとっても、たとえばescキーを押すとモーダルダイアログを閉じられるといった小さな心配りひとつが、アプリケーションの印象に大きく影響することもあります。
Stimulusにはデフォルトのホットキー機能が組み込まれていません。私が最近手掛けたArrows.toプロジェクトの一環として評価を行ったホットキーのエコシステムについて、私の考えを共有したいと思います。
1. stimulus-hotkeysパッケージ
stimulus-hotkeysパッケージはhotkeys
コントローラを提供し、JSONオブジェクトを用いてキーボードショートカットをStimulusアクションにマッピングします。
<div
data-controller="hotkeys"
data-hotkeys-bindings-value='{"ctrl+z, command+z": "#foo->editor#undo"}'
></div>
<div id="foo" data-controller="editor"></div>
bindings
マップの構文はStimulusアクションの構文と似ていますが、Stimulusコントローラを探索してアクションを呼び出すためにselector
を追加します。内部では、stimulus-hotkeys
がより一般性の高いHotKeys.JSライブラリを使っているので、あらゆるキーの組み合わせ、モディファイアキー、スコープをサポートしています。
私がこのライブラリで気に入った点は、ショートカットのマッピングをHTMLマークアップ内に記述することです。これはHotwireの精神を強く感じさせるからです。このライブラリはStimulusコントローラに余分なコードを一切追加しないので、他のサードパーティStimulusコントローラと組み合わせるときの相性も最高です。
惜しい点は、ハッシュ風のデータ構造をHTML属性に追加するので構文がトリッキーになることです。私はJSON構文が好きではありません。
2. stimulus-use/useHotkeys
stimulus-useプロジェクトは、Stimulus向けの再利用可能な振る舞いのコレクションです。Reactに親しんでいる人なら、Reactのhooks
システムに似たものをこのプロジェクトに感じるかもしれませんが、Stimulus向けである点が異なります。
このコレクションにはuseHotkeys
も含まれています。stimulus-hotkeys
と同様に、重たい仕事はHotKeys.JSが行ってくれます。
こちらは、必要なホットキーとそれに対応するハンドラを以下のように自分のStimulusコントローラ内に定義する必要があります。
import { Controller } from '@hotwired/stimulus'
import { useHotkeys } from 'stimulus-use'
export default class extends Controller {
connect() {
useHotkeys(this, {
'cmd+t': [this.openPalette],
'.': [this.editFile]
})
}
}
このライブラリはStimulusコントローラに置くのにうってつけで、コントローラが切断されたときにショートカットの登録を自動的に解除してくれます。どんなホットキーを設定しているかがひと目でわかるので、自分のアプリケーションコントローラにホットキーを追加するオプションとしてよくできていると感じられました。
ただ私たちのニーズではひとつ惜しい点がありました。私たちはページに常駐するコントローラを使っているのですが、非表示の場合はホットキーが効かないようにする必要があったのです。useHotKey
はStimulusコントローラのライフサイクルを扱いますが、キーボードイベントを動的にバインドしたりバインド解除したりしようとするときの柔軟性に欠けます。
3. HotKeys.JSを直接使う
Stimulusに特化した人気のソリューション2つを調べた結果、どちらも同じHotKeys.jsに依存していることがわかったので、HotKeys.jsのソースをストレートに使うことに決めました。
import { Controller } from '@hotwired/stimulus'
import hotkeys from 'hotkeys-js'
export default class extends Controller {
connect() {
hotkeys("esc", () => this.doSomething())
}
disconnect() {
hotkeys.unbind("esc")
}
}
このライブラリを直接追加するのは簡単ですが、必ずunbind
を呼び出してイベントを明示的にクリーンアップする必要がある点に注意してください。さもないとホットキー関数のインスタンスが複数作成されてしまいます。
基本的なホットキーを1個か2個追加する程度のシンプルなケースであれば、このアプローチは完璧に有効です。私たちのアプリケーションではショートカットのバインドやバインド解除を細かく制御する必要があったので、最終的にこのソリューションに落ち着きました。
4. github/hotkey
私が評価を行った最後のオプションは、GitHubのhotkeyという小さなライブラリです。これはStimulusやHotwireだけに特化したものではありません。
<button data-hotkey="Shift+?">Show help dialog</button>
<a href="/page/2" data-hotkey="j">Next</a>
<a href="/help" data-hotkey="Control+h">Help</a>
<a href="/rails/rails" data-hotkey="g c">Code</a>
<a href="/search" data-hotkey="s,/">Search</a>
私はBasecampが運営するHEY.comのsourcemapでこのライブラリを見つけ、GitHubのメインRailsアプリで実地のバトルテストを経ていることを知りました。同READMEには「GitHubのほぼすべてのページで使われている」と書かれています。
このライブラリのdata-hotkey
属性は、今回紹介する4つのオプションの中で最も構文がすっきりしています。ライブサイクルイベントを扱うためにStimulusコントローラのラッパーが必要ですが、install
とuninstall
を呼び出すぐらいにしか使いません。
このライブラリの設計はブラウザのデフォルトの振る舞いに強く依存しています。任意のJavaScriptやStimulusアクションを呼び出す代わりに、github/hotkey
がターゲットとなるHTML要素のアクションをトリガーします(ターゲットがリンクならリンク先を開く、ボタンなら送信する、入力フィールドならフォーカスを置くなど)。
ブラウザのネイティブ機能を利用する形で自分のマークアップを構成できるのであれば、このライブラリはよいオプションのひとつです。ただし、既存のStimulusに直接フックをかける方法はありません。
まとめ
どのライブラリを選択するかは、ホットキーで何が必要かによって変わります。
- stimulus-hotkeys
- 追加するだけで利用でき、JavaScriptコードの追加が一切不要
- stimulus-use/useHotKeys
- StimulusネイティブのHotKey.js APIラッパーとして使うのに便利
- HotKey.js
- 強力なキーイベント抽象化を自分のコードにミックスイン可能
- github/hotkey
- ブラウザネイティブの振る舞いに強く依存するが、構文は最高
最終的には、自分が作りたいものとのトレードオフを見極める必要があります。私はHotKey.jsを直接使うことにしましたが、これが要件に最も見合っていたからです。
本記事が役に立つと思った方は、ぜひフォームでニュースレターの登録をお願いします。余分な文章を削ぎ落とした読みやすく価値ある情報だけをニュースレターで配信いたします。
概要
原著者の許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。