NumberFieldについて - React Ariaの実装読むぞ
こんにちは、フロントエンドエンジニアの mehm8128 です。
今日は NumberField について書いていきます。
useNumberField
とは
数値の入力欄を作るための hook で、様々な format や i18n をサポートしています。
使用例
ドキュメントからそのまま取ってきています。
import { useNumberFieldState } from "react-stately";
import { useLocale, useNumberField } from "react-aria";
// Reuse the Button from your component library. See below for details.
import { Button } from "your-component-library";
function NumberField(props) {
let { locale } = useLocale();
let state = useNumberFieldState({ ...props, locale });
let inputRef = React.useRef(null);
let {
labelProps,
groupProps,
inputProps,
incrementButtonProps,
decrementButtonProps,
} = useNumberField(props, state, inputRef);
return (
<div>
<label {...labelProps}>{props.label}</label>
<div {...groupProps}>
<Button {...decrementButtonProps}>-</Button>
<input {...inputProps} ref={inputRef} />
<Button {...incrementButtonProps}>+</Button>
</div>
</div>
);
}
本題
APG はこちらです。
role
input
のtype
をnumber
ではなくてtext
にしているので、spinbutton
role ではなくてtextbox
role になっています。これは後述する色々なフォーマットに対応するためです。
その代わりに、要素の役割についてスクリーンリーダーの読み上げ用に補足説明を入れるaria-roledescription
が利用されています。今回の場合、日本語では「数値フィールド」と読み上げられるようになっています。
また、useSpinButton
という hooks から返されるspinButtonProps
によってspinbutton
role に上書きすることも可能なのですが、React Aria の実装ではさらにそれをrole: null
で上書きしてデフォルトのtextbox
role にしています。これは、Voice Over 利用時にspinbutton
role にフォーカスできなくなってしまっていることが理由らしいです。
ちなみに、+/-ボタンはキーボードの矢印キーでインクリメント・デクリメントの操作が可能なことから Tab フォーカスされないようになっています。
さらに、spinbutton
role ではないので、spinButtonProps
から返されるaria-valuemax
などのaria-
属性もnull
に上書きしています。
様々な format
ドキュメントにもあるように、小数点やパーセント表記、通貨、その他の単位のフォーマットがサポートされています。この変換を行ったり、+/-ボタンによるインクリメント・デクリメントをサポートしたりするために、useNumberFieldState
という hook が用意されています。
数値フィールド内の値はnumberValue
とinputValue
という 2 つの state で管理されています。numberValue
は内部で持つ用のnumber
型の値、inputValue
は表示用のstring
型の値で、後者は単位がついたりしているものです。
どちらもuseNumberFieldState
内でuseState
を用いて管理されています。numberValue
はuseSpinButton
に渡されてspinButtonProps
のaria-valuenow
に用いられ、inputValue
はinputProps
としてuseNumberField
から返されてinput
要素に渡されます。
onWheel
タッチパッド の マウスホイールを上下に動かすことで increment/decrement ができますが、タッチパッドのスクロール操作(一般的に指 2 本でやるやつ)でももちろんできます。
しかし、タッチパッドだと上下以外にも横方向へのスクロールや、斜め方向へのスクロールもできるので、誤って操作してしまうのを防ぐために、横方向のスクロール(e.deltaX
)が縦方向のスクロール(e.deltaY
)より大きい場合は increment/decrement されないようになっています。
inputMode
昨日紹介した inputMode
です。
今回は数値に特化しているので hook 内部で指定しています。
しかし、ブラウザや OS によって同じ inputMode
でも表示されるキーボードが異なってしまうので、負の値を許容する NumberField のときは-
ボタンが表示されるキーボードを表示する、とかをちゃんと条件分岐して設定しています。
具体的に説明します。
iPhone ではinputMode
がnumeric
でもdecimal
でも-
ボタンが表示されないので、負の値を許容する場合はinputMode
をtext
にします。
Android ではinputMode
がnumeric
の場合に-
ボタンがあり、decimal
のときにはないので、負の値を許容する場合はinputMode
をnumeric
にします。
細かいところですがしっかり各端末の挙動が調査されて適切な設定がされていることが分かります。
公式のブログ記事にもまとめられていました(Mobile
セクション以外は i18n の回で解説予定です)。
まとめ
明日の担当は @mehm8128 さんで、 Radio と Checkbox についての記事です。お楽しみにー
Discussion