要素
Table
Table は、アプリが子要素として書く数行(請求書、サマリー、比較表など)をレイアウトし、スクリーンリーダーにテーブルとして伝えます。テーブル本体、行のグループ、行、列ヘッダー、セルが、それぞれのロールとともに公開されます。構成要素は TableHeader、TableBody、TableFooter(行のグループ)、TableRow(セルを横に並べたもの)、TableHead(列のヘッダーセル)、TableCell、TableCaption です。見た目はアプリ側のものです。どの部品にも独自の色、枠線、パディング、幅はなく、それぞれアプリの class と style を受け取ります。多数の行、ソート、選択、サイズ変更可能な列が必要な場合は、DataTable がテーブルです。
import { mount, Text, View } from "nsh";
import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "nsh/base";
import { size, Stage, ui } from "./palette";
// A few rows the app writes out: the parts give screen readers a table, the
// classes and the palette give it its look.
const invoices = [
{ id: "INV001", status: "Paid", method: "Card", amount: 250 },
{ id: "INV002", status: "Pending", method: "Transfer", amount: 150 },
{ id: "INV003", status: "Unpaid", method: "Card", amount: 350 },
{ id: "INV004", status: "Paid", method: "Cash", amount: 450 },
];
const widths = ["w-24", "flex-1", "w-24", "w-24"];
function Cell(props: { i: number; head?: boolean; end?: boolean; children: string }) {
const style = { fontSize: size.label, color: props.head ? ui().muted : ui().text, fontWeight: props.head ? ("500" as const) : ("400" as const) };
const Part = props.head ? TableHead : TableCell;
return (
<Part class={`${widths[props.i]} px-3 py-2.5`}>
<Text style={{ ...style, textAlign: props.end ? "right" : "left" }}>{props.children}</Text>
</Part>
);
}
function App() {
const line = { borderBottomWidth: 1, borderColor: ui().line };
const total = invoices.reduce((sum, i) => sum + i.amount, 0);
return (
<Stage width={520}>
<View style={{ ...ui().card, borderRadius: 12, overflow: "hidden" }}>
<Table accessibilityLabel="Recent invoices">
<TableHeader>
<TableRow style={line}>
{["Invoice", "Status", "Method", "Amount"].map((title, i) => (
<Cell i={i} head end={i === 3}>
{title}
</Cell>
))}
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((inv) => (
<TableRow style={line}>
<Cell i={0}>{inv.id}</Cell>
<Cell i={1}>{inv.status}</Cell>
<Cell i={2}>{inv.method}</Cell>
<Cell i={3} end>{`$${inv.amount.toFixed(2)}`}</Cell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<Cell i={0} head>Total</Cell>
<Cell i={1}>{""}</Cell>
<Cell i={2}>{""}</Cell>
<Cell i={3} end>{`$${total.toFixed(2)}`}</Cell>
</TableRow>
</TableFooter>
<TableCaption class="px-3 py-2">
<Text style={{ fontSize: size.detail, color: ui().muted }}>A list of your recent invoices.</Text>
</TableCaption>
</Table>
</View>
</Stage>
);
}
mount(App);use nsh_base::{table, table_body, table_cell, table_head, table_header, table_row, theme};
use nsh_core::prelude::*;
fn invoices() -> impl IntoElement {
let cell = |label: &'static str| table_cell().w(px(120.0)).px_3().py_2().child(text(label));
table()
.accessibility_label("Recent invoices")
.child(
table_header().child(
table_row()
.border_b_1()
.border_color(move || theme().border)
.child(table_head().w(px(120.0)).px_3().py_2().child(text("Invoice")))
.child(table_head().w(px(120.0)).px_3().py_2().child(text("Amount"))),
),
)
.child(table_body().child(table_row().child(cell("INV001")).child(cell("$250.00"))))
}import { mount, Text } from "nsh";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "nsh/base";
function App() {
return (
<Table accessibilityLabel="Recent invoices">
<TableHeader>
<TableRow class="border-b border-border">
<TableHead class="w-32 px-3 py-2"><Text>Invoice</Text></TableHead>
<TableHead class="w-32 px-3 py-2"><Text>Amount</Text></TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell class="w-32 px-3 py-2"><Text>INV001</Text></TableCell>
<TableCell class="w-32 px-3 py-2"><Text>$250.00</Text></TableCell>
</TableRow>
</TableBody>
</Table>
);
}
mount(App);Table とその部品は、Rust の nsh-base クレートである nsh/base から提供されます。table()、table_header()、table_body()、table_footer()、table_row()、table_head()、table_cell()、table_caption() がそれらを作り、型付きのスタイルメソッドと .child(..) を使えます。行はセルを横に並べてレイアウトします。セルの幅はアプリが指定するので、列がそろいます。
#Props
| Prop | Rust | 型 | デフォルト | 説明 |
|---|---|---|---|---|
accessibilityLabel | .accessibility_label(..) | string | なし | Table のみ:スクリーンリーダーがテーブルをどう呼ぶか。 |
children | .child(..) | JSX.Element | なし | 部品が保持する内容:Table ではグループとキャプション、グループでは行、行ではセル、セルではコンテンツ。 |
class、style | スタイルメソッド | string、StyleProp<ViewStyle> | なし | 部品の見た目とレイアウト。 |
#部品
| 部品 | 配置先 | スクリーンリーダー向けのロール |
|---|---|---|
Table | table | |
TableHeader | Table | 行グループ:列ヘッダーの行 |
TableBody | Table | 行グループ:データの行 |
TableFooter | Table | 行グループ:合計、サマリー行 |
TableRow | グループ、または Table | 行。セルを横に並べる |
TableHead | ヘッダーの TableRow | 列ヘッダー |
TableCell | TableRow | セル |
TableCaption | Table | アプリがテーブルについて表示するコンテンツ |
各プラットフォームは、そのスクリーンリーダー向けにロールを割り当てます。Linux(AT-SPI)、Web(ARIA)、Android、GPUI バックエンドでは table、row、cell、column header の各ロール、macOS と Windows ではグリッドとグループ、iOS ではヘッダーとしてのヘッダーです。
#関連項目
- DataTable:行数・列数に制限のないテーブル。選択、ソート、サイズ変更や移動が可能な列に対応します。
- Accessibility:
accessibilityRoleの値、table、row、cell、columnheader、rowgroupなどを扱います。