編集可能データテーブルEditableDataTableExperimental
Editable line-item grid: rows of consumer-rendered editor cells with add/remove row, a totals/footer row, per-cell accessible labels, and a desktop table that stacks into mobile cards. For invoices, journals, estimates, and timesheets.
プレビュー
Props
表は横にスクロールできます
| プロパティ | 型 | 初期値 | 説明 |
|---|---|---|---|
| columns | EditableColumn<TRow>[] | - | Column defs. Each renders its cell editor via cell(row, ctx); ctx.ariaLabel is a ready-made label. |
| rows | TRow[] | - | The (controlled) row data — the consumer owns it. |
| getRowId | (row, index) => string | - | Stable row key. |
| onAddRow | () => void | - | Shows an add-row button when set. |
| onRemoveRow | (index) => void | - | Shows a per-row remove button (hidden at/below minRows). |
| minRows | number | 0 | Minimum rows kept. |
| getRowError | (row, index) => string | undefined | - | Marks a row invalid; the message is exposed to screen readers. |
| footer | ReactNode | - | Totals / balance row, rendered under the body on desktop + mobile. |
| renderRowCard | (row, ctx) => ReactNode | - | Custom mobile card body (defaults to stacking each column). |
| variant | "default" | "compact" | "default" | Density. |
| labels / rowLabel / caption / className | — | - | Add/remove/empty labels, per-row label, caption, extra classes. |
Usage
import { EditableDataTable, type EditableColumn, Input, NumberInput, formatCurrency } from "@gunjo/ui"
type LineItem = { id: string; name: string; qty: number; price: number }
export function Example() {
const [rows, setRows] = React.useState<LineItem[]>([/* ... */])
const update = (i: number, patch: Partial<LineItem>) =>
setRows((rs) => rs.map((r, idx) => (idx === i ? { ...r, ...patch } : r)))
const columns: EditableColumn<LineItem>[] = [
{ id: "name", header: "品目",
cell: (row, ctx) => <Input value={row.name} aria-label={ctx.ariaLabel}
onChange={(e) => update(ctx.rowIndex, { name: e.target.value })} /> },
{ id: "qty", header: "数量", align: "right",
cell: (row, ctx) => <NumberInput value={row.qty} aria-label={ctx.ariaLabel}
onValueChange={(v) => update(ctx.rowIndex, { qty: v })} /> },
{ id: "amount", header: "金額", align: "right",
cell: (row) => formatCurrency(row.qty * row.price) },
]
return (
<EditableDataTable columns={columns} rows={rows} getRowId={(r) => r.id} minRows={1}
onAddRow={() => setRows((rs) => [...rs, makeRow()])}
onRemoveRow={(i) => setRows((rs) => rs.filter((_, idx) => idx !== i))}
footer={<Totals rows={rows} />} />
)
}