SegmentedButtonGroup
import { useState } from 'react'
import { Icon, SegmentedButtonGroup, Surface, Text } from '@language-lit/material3-expressive'
const viewSegments = [
{ value: 'day', label: 'Day' },
{ value: 'week', label: 'Week' },
{ value: 'month', label: 'Month' },
]
const alignSegments = [
{ value: 'left', label: 'Left', icon: <Icon source="format_align_left" /> },
{ value: 'center', label: 'Center', icon: <Icon source="format_align_center" /> },
{ value: 'right', label: 'Right', icon: <Icon source="format_align_right" /> },
]
const filterSegments = [
{ value: 'walk', label: 'Walk' },
{ value: 'bike', label: 'Bike' },
{ value: 'transit', label: 'Transit' },
{ value: 'drive', label: 'Drive' },
]
const notificationSegments = [
{ value: 'email', label: 'Email', icon: <Icon source="mail" /> },
{ value: 'sms', label: 'SMS', icon: <Icon source="sms" /> },
{ value: 'push', label: 'Push', icon: <Icon source="notifications" /> },
]
export function SegmentedButtonGroupExample() {
const [view, setView] = useState('day')
const [align, setAlign] = useState('left')
const [filters, setFilters] = useState<readonly string[]>(['walk'])
const [notifications, setNotifications] = useState<readonly string[]>(['email', 'push'])
return (
<Surface
as="section"
aria-labelledby="segmented-button-group-example-title"
color="surface-container-low"
shape="extra-large"
className="segmented-button-group-example"
>
<Text
as="h2"
id="segmented-button-group-example-title"
variant="titleLarge"
emphasis="emphasized"
>
Segmented button groups
</Text>
<Text as="p" variant="bodyMedium">
Single- and multi-choice groups built on native radio and checkbox
inputs, with grouped corner shaping and an icon-to-checkmark
crossfade.
</Text>
<div className="segmented-button-group-example__row">
<SegmentedButtonGroup
segments={viewSegments}
aria-label="View"
value={view}
onValueChange={setView}
/>
<SegmentedButtonGroup
segments={alignSegments}
aria-label="Alignment"
value={align}
onValueChange={setAlign}
/>
</div>
<div className="segmented-button-group-example__row">
<SegmentedButtonGroup
multiple
segments={filterSegments}
aria-label="Travel modes"
value={filters}
onValueChange={setFilters}
/>
</div>
<div className="segmented-button-group-example__row">
<SegmentedButtonGroup
segments={viewSegments}
aria-label="Unavailable view"
defaultValue="week"
disabled
/>
<SegmentedButtonGroup
segments={[
{ value: 'day', label: 'Day' },
{ value: 'week', label: 'Week', disabled: true },
{ value: 'month', label: 'Month' },
]}
aria-label="Partly unavailable view"
defaultValue="day"
/>
</div>
<div className="segmented-button-group-example__row">
<SegmentedButtonGroup
multiple
segments={notificationSegments}
aria-label="Notification channels"
value={notifications}
onValueChange={setNotifications}
/>
<SegmentedButtonGroup
multiple
segments={filterSegments}
aria-label="Unavailable travel modes"
defaultValue={['walk']}
disabled
/>
</div>
</Surface>
)
}SegmentedButtonGroupは宣言的なsegments配列からMaterialのセグメントボタンを並べます。相互排他的な選択にはネイティブのラジオグループを、複数選択には独立したネイティブのチェックボックスを使います。複合的な子要素APIはなく、内容、順序、項目数はすべてsegmentsで指定します。
import { SegmentedButtonGroup } from '@language-lit/material3-expressive'
import '@language-lit/material3-expressive/styles.css'
<SegmentedButtonGroup
segments={[
{ value: 'day', label: 'Day' },
{ value: 'week', label: 'Week' },
{ value: 'month', label: 'Month' },
]}
aria-label="View"
value={view}
onValueChange={setView}
/>仕様
- 各セグメントはネイティブの
<input type="radio">(単一選択、既定)または<input type="checkbox">(multiple: true)を1つずつ出力し、ネイティブの<label>で囲みます。表示テキストがそのコントロールのアクセシブルネームになるため、個別にidを設定する必要はありません。 - グループのルートには
role="radiogroup"またはrole="group"が付きます。aria-label/aria-labelledbyでアクセシブルネームを指定します。 value/defaultValue/onValueChangeは、単一選択モードではstring、複数選択モードではreadonly string[]です。multipleのリテラル値で判別するため、型レベルで2つの形式を混同できません。nameの既定値は生成されたIDで、すべてのセグメントのコントロールに共通です。フォームの送信フィールド名を指定するには独自のnameを渡します。- グループの
disabledはすべてのセグメントを無効にします。個々のセグメントに指定したdisabledはその項目だけを無効にします。 classNameとstyleはグループのルートに適用されます。refも同じルート要素を参照します。
選択モード
multiple | コントロール | 選択 |
|---|---|---|
false(既定) | <input type="radio">、共通のname | 相互排他的。独自のキー処理を追加せず、ネイティブのフォーカス移動キーボード動作を使用 |
true | <input type="checkbox">、共通のname | 個別に選択。各コントロールに順番にフォーカスできる |
選択はすべてネイティブのため、非制御の単一選択グループは、コンポーネントが再レンダーしなくてもブラウザーが排他選択を保ちます。これは、このライブラリのRadioがすでに利用している保証と同じです。ネイティブフォームのリセットでは、ライブラリ独自の状態管理に関係なく、各コントロールの既定選択に戻ります。
<SegmentedButtonGroup
multiple
segments={travelModes}
aria-label="Travel modes"
value={filters}
onValueChange={setFilters}
/>単一選択は共通のnameで1つの値を送信します。複数選択では、選択された値をFormData.getAll(name)で取得できます。
シェイプ
最初のセグメントは論理方向の開始側だけ、最後のセグメントは終了側だけを角丸にし、中間のセグメントは四角形のままにします。1つだけの場合は全角を丸めます。物理方向ではなく論理コーナーのプロパティを使うため、RTLでも正しく反転します。隣り合うセグメントは境界線幅分だけ重なり、共有辺が二重にならないようにします。選択中または操作中のセグメントの境界線は隣の項目より前面に表示されます。
アイコン
<SegmentedButtonGroup
segments={[
{ value: 'left', label: 'Left', icon: <Icon source="format_align_left" /> },
{ value: 'center', label: 'Center', icon: <Icon source="format_align_center" /> },
]}
aria-label="Alignment"
value={align}
onValueChange={setAlign}
/>iconを指定しないセグメントは、組み込みのチェックマークだけを表示します。選択時にフェードインしながら拡大し、選択解除時はすぐに消えます。iconを指定すると、未選択時にはそのアイコンを表示し、選択時にはチェックマークへクロスフェードします。切り替えはどちらの方向でも行います。
トークンと境界
色、形状、モーションの値は--m3e-comp-segmented-button-group-*の1つの登録にまとめられています。テーマの上書きはMaterial3Providerのスコープ内で適用されます。SegmentedButtonGroupは実行時スタイルを注入せず、Next.js、Vite、ルーター、アニメーションライブラリ、非公開のアプリケーションコードをインポートしません。