Date Input
年・月・日を独立したセグメントとして編集する、WAI-ARIA の spinbutton パターンに従った unstyled 部品です。3 セグメントすべてが揃った場合のみ実在する日付か検証し、存在しない日付(例: 2 月 30 日)はセグメント値を保持したまま invalid 表示に倒します(fail-closed)。
ark-ui(zag.js の date-input machine)の Data Attributes 表・キーボード操作と突合済みです(イシュー #1626)。data-type(year/month/day)・data-value(入力済み)・data-editable(常時)・data-placeholder-shown(未入力)を各セグメントへ、data-focus を control/segment-group へ、data-readonly を全パーツへ付与します。状態機械の dispatch 語彙として increment/decrement(境界で wrap-around)・page-increment/page-decrement(PageUp/PageDown 相当、境界で clamp)・home/end・prev/next(矢印キーでのセグメント間移動相当)・backspace を提供しますが、実 DOM キーイベントへの配線(fandhe-frontend-wasm-full)は本部品の責務外です。
Themes 版(fandhe-frontend-pre-styled-ui)はこの構造へ既定 CSS を追加するだけの薄いラッパーであり、CSS は持ちません。スタイル済みの表示例は Date Input を参照してください。
headless-ui はスタイルレスです。data-scope/data-part/data-* をセレクタに使い、自前の CSS を当てることができます(例: [data-scope="date-input"][data-part="segment"][data-type="year"])。
実在性検証以外の入力値検証・ロケール依存の日付整形・フォーム送信処理は本部品の責務外であり、利用側が担います。
関連 API: fandhe-frontend-headless-ui API
Demo
枠線・余白は docs サイト側のデモ枠であり、fandhe-frontend-headless-ui 自体はスタイルを持ちません。
Features
- Root/Label/Control/SegmentGroup/Segment/HiddenInput の 6 anatomy パーツと、年・月・日をそれぞれ独立したセグメントとして編集する状態機械
DateInputを提供する。 - 各
segmentはrole="spinbutton"+aria-valuemin/aria-valuemaxを常に出力し、値が入力済みのときのみaria-valuenowを追加する(WAI-ARIA spinbutton パターン)。 - 3 セグメントすべてが揃った場合のみ実在する日付か検証する(fail-closed)。存在しない日付(例: 2 月 30 日)は
is_invalidがtrueを返す状態としてセグメント値を保持したまま表示する(値を破棄しない)。 hidden_inputパーツのみがnameを持ち、確定済み日付を ISO 8601 文字列としてフォーム送信へ渡す。各segment自体はnameを持たずフォーム送信に参加しない。- ark-ui(zag.js
date-inputmachine)の Data Attributes 表と突合し、data-type(year/month/day)・data-value(入力済み)・data-editable(常時)・data-placeholder-shown(未入力)をsegmentへ、data-focusをcontrol/segment-groupへ、data-readonlyを全パーツへ追加した(イシュー #1626)。 Increment/Decrementは境界で wrap-around する(例: year は 9999→0)。PageIncrement/PageDecrement(PageUp/PageDown 相当)・Home/End・Prev/Next(矢印キーでのセグメント間移動相当)・Backspaceの状態遷移語彙を提供するが、実 DOM キーイベントへの配線はfandhe-frontend-wasm-full側の責務(本コンポーネントは状態機械の dispatch 語彙のみを提供する)。- セグメント値の実在性チェック以外の入力値検証・ロケール依存の日付整形は本コンポーネントの責務外であり、利用者側が担う(
docs/policy/intentional-non-adoption.md§3.25)。
Anatomy
root
label
control
segment-group
segment
hidden-inputAPI Reference
Arguments
| Name | Type | Default | Description |
|---|---|---|---|
kind | DateSegment | (必須) | Year/Month/Day のいずれか。aria-label・未入力時のプレースホルダ・data-type を内部で決定する。 |
value | Option<&str> | None | 現在の表示値。None のとき data-placeholder-shown を付与しプレースホルダを表示する。Some のとき data-value を付与する。 |
min / max | &str | (必須) | aria-valuemin/aria-valuemax に出力する下限・上限の文字列表現。 |
props | DateInputProps | DateInputProps::default() | disabled/readonly/invalid/focused の 4 フラグを束ねる構造体(root/label/control/segment_group/segment 共通)。focused は control/segment-group の data-focus にのみ反映され、root/label/segment は無視する。 |
Data Attributes
| Part | Attribute | Observed Values |
|---|---|---|
control | data-disabled | (値なし) |
control | data-focus | (値なし) |
control | data-invalid | (値なし) |
control | data-readonly | (値なし) |
hidden-input | data-disabled | (値なし) |
label | data-disabled | (値なし) |
label | data-invalid | (値なし) |
label | data-readonly | (値なし) |
root | data-disabled | (値なし) |
root | data-invalid | (値なし) |
root | data-readonly | (値なし) |
segment | data-disabled | (値なし) |
segment | data-editable | (値なし) |
segment | data-invalid | (値なし) |
segment | data-placeholder-shown | (値なし) |
segment | data-readonly | (値なし) |
segment | data-type | day, month, year |
segment | data-value | (値なし) |
segment-group | data-disabled | (値なし) |
segment-group | data-focus | (値なし) |
segment-group | data-invalid | (値なし) |
segment-group | data-readonly | (値なし) |
Examples
実在しない日付(invalid)の表示
年・月・日すべてが揃っても実在しない日付(2 月 30 日)の場合、Day セグメントへ aria-invalid・data-invalid が付与されます。セグメント値自体は保持されたままです。
Custom CSS
headless-ui が出力する data-* 属性を CSS セレクタとして利用する最小例です。
headless-ui はスタイルレスです。data-scope/data-part/data-* をセレクタに使い、以下のような CSS を自前で当てられます。
[data-scope="date-input"][data-part="segment"] {
border-radius: 4px;
padding: 0 4px;
}
[data-scope="date-input"][data-part="segment"][data-type="year"] {
min-width: 4ch;
}
[data-scope="date-input"][data-part="segment"][data-placeholder-shown] {
color: #999;
}
[data-scope="date-input"][data-part="segment-group"][data-focus] {
outline: 2px solid #06c;
}
[data-scope="date-input"][data-part="root"][data-invalid] [data-part="segment-group"] {
border-color: #c00;
}
[data-scope="date-input"][data-part="segment"]:focus-visible {
outline: 2px solid #06c;
outline-offset: 2px;
}Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Tab | 各 segment(div role="spinbutton")は disabled でない限り tabindex="0" を持ち、ブラウザ既定の Tab 順に含まれる。 |
| ArrowUp / ArrowDown | フォーカス中セグメントを ±1("increment"/"decrement" dispatch)。境界で wrap-around する。実 DOM 配線は wasm-full 側。 |
| PageUp / PageDown | フォーカス中セグメントを ±PAGE_STEP("page-increment"/"page-decrement" dispatch、境界で clamp)。実 DOM 配線は wasm-full 側。 |
| Home / End | フォーカス中セグメントを最小値/最大値へ("home"/"end" dispatch)。実 DOM 配線は wasm-full 側。 |
| ArrowLeft / ArrowRight | フォーカスを前後のセグメントへ移動("prev"/"next" dispatch、year↔month↔day の順・端で留まる)。実 DOM 配線は wasm-full 側。 |
| Backspace / Delete | 値があれば消去し留まる。既に未入力なら前のセグメントへフォーカス移動("backspace" dispatch、Delete も同一 dispatch にマップする配線側契約)。実 DOM 配線は wasm-full 側。 |
WAI-ARIA
| Attribute | Description |
|---|---|
| role (segment) | 各 segment へ "spinbutton" を固定付与する。 |
| role (segment-group) | segment_group へ "group" を固定付与する。aria-labelledby は呼び出し側が attrs 経由で label の id を配線する。 |
| aria-valuemin / aria-valuemax | 各 segment へ常に出力する下限・上限。 |
| aria-valuenow | value が Some のときのみ segment へ出力する。 |
| aria-invalid | props.invalid が true のとき segment へ "true" を付与する。 |
| aria-readonly | props.readonly が true のとき segment へ "true" を付与する。 |
| aria-disabled | props.disabled が true のとき segment へ "true" を付与する(tabindex は省略される)。 |