DateFieldについて - React Ariaの実装読むぞ
こんにちは、フロントエンドエンジニアの mehm8128 です。
今日は DateField について書いていきます。
使用例
ドキュメントからそのまま取ってきています。
export function DateField(props) {
let { locale } = useLocale();
let state = useDateFieldState({
...props,
locale,
createCalendar,
});
let ref = React.useRef(null);
let { labelProps, fieldProps } = useDateField(props, state, ref);
return (
<div className="wrapper">
<span {...labelProps}>{props.label}</span>
<div {...fieldProps} ref={ref} className="field">
{state.segments.map((segment, i) => (
<DateSegment key={i} segment={segment} state={state} />
))}
{state.isInvalid && <span aria-hidden="true">🚫</span>}
</div>
</div>
);
}
function DateSegment({ segment, state }) {
let ref = React.useRef(null);
let { segmentProps } = useDateSegment(segment, state, ref);
return (
<div
{...segmentProps}
ref={ref}
className={`segment ${segment.isPlaceholder ? "placeholder" : ""}`}
>
{segment.text}
</div>
);
}
本題
i18n
フォーマットについては昨日の記事で紹介したのですが、Intl 以外の観点で見ていきます。
React Aria ではCalendarDate
オブジェクトという、Date
オブジェクトを wrap しているのではなくて完全に独自実装をしている日付用オブジェクトを用意しています。これは Temporal に inspire されているらしく、日付の演算やその他便利なメソッドが用意されています。
Rather than wrapping a Date object and providing an API on top, it implements all date arithmetic and utilities from scratch.
公式のサンプルコードもそのまま載せておきます。
import { CalendarDate } from "@internationalized/date";
let date = new CalendarDate(2022, 2, 3);
date = date.add({ years: 1, months: 1, days: 1 });
date.toString(); // '2023-03-04'
CalendarDate
オブジェクトのいくつかの利点を見ていきます。
Intl.datetimeformat
は複数の暦のフォーマットをサポートしているのですが、Date
オブジェクトはグレゴリオ暦のみサポートしているので、演算結果を別の暦で表示しようとすると正しく表示されないという問題があります。
よって、CalendarDate
オブジェクトを用いて別の暦に変換できるようにしているとのことです。
サンプルコードをそのまま引用しておきます。
import {
GregorianCalendar,
HebrewCalendar,
toCalendar,
} from "@internationalized/date";
let hebrewDate = new CalendarDate(new HebrewCalendar(), 5781, 1, 1);
let gregorianDate = toCalendar(hebrewDate, new GregorianCalendar());
gregorianDate.toString();
// => '2020-09-19'
その他、1 週間が何曜日に終わるかや何曜日が休日か、またタイムゾーンとかサマータイムなどの面倒も見てくれていて、様々なユーティリティ関数が提供されています。
useDateSegment
useDateSegment
は年、月、日などのそれぞれの入力欄用の hook です。
この中でuseSpinButton
を用いて spinbutton にしていたり、その他数値の入力に関する a11y 対応がされています。
DatePicker で使用するときの冗長な読み上げ対応
useDatePicker
という hook を使うことで、DateField と一緒に Calendar(明日紹介します)を表示することができる、DatePicker を作成することができます。
このuseDatePicker
で使用するときに、冗長な読み上げがされないような対応がされています。
useDateField
を単体で利用するときは年・月・日の入力欄のみで 1 つのグループですが、useDatePicker
と一緒に利用するときは、DatePicker のトリガーボタンも含めて 1 つのグループなので、group
role をつけて description などを付与するのを、useDateField
の責務ではなくてuseDatePicker
の責務にして、冗長な読み上げを防いだり、グループ構造を適切にしています。
以下のコードで、descProps
は「選択した日付 : 2024 年 12 月 18 日」などのテキスト、fieldProps
はフィールド自体の説明文です。
hookData.set
は先ほどのuseDateSegment
にデータを渡しています。
ここで DateField の role などを指定しています。
まとめ
明日の担当は @mehm8128 さんで、 Calendar についての記事です。お楽しみにー
Discussion