Data Table
データテーブル。<x-table> 親 + <x-table.th> / <x-table.td> / <x-table.empty> の入れ子構成で、カード面・ヘッダ行・行間リズム・行ホバー・最終行の枠線処理・数値列の右寄せ (tabular-nums)・端カラムの余白 (x-card の p-lg と一致)・空状態プレースホルダまでを 1 コンポーネントが持つ。本文の行は素の <tr> のままなので @forelse / wire:key がそのまま使える。size (sm/md/lg)・card・hover の 3 軸。
基本形 — table 自身がカード面を持つ
| Workspace | Members | Seats | Plan | Alerts | |
|---|---|---|---|---|---|
| Acme Marketing | 18 | 5 / 10 | Active | ||
| Northwind Sales | 7 | 7 / 7 | Seats full | ||
| Globex Support | 42 | 31 / 50 | Active | ||
| Initech Research | 3 | 1 / 5 | Trial |
<x-table>
<x-slot:head>
<x-table.th>Workspace</x-table.th>
<x-table.th numeric>Members</x-table.th>
<x-table.th numeric>Seats</x-table.th>
<x-table.th>Plan</x-table.th>
<x-table.th align="right">Alerts</x-table.th>
<x-table.th action />
</x-slot:head>
<tr>
<x-table.td><span class="font-medium">Acme Marketing</span></x-table.td>
<x-table.td numeric muted>18</x-table.td>
<x-table.td numeric>
<span class="font-medium">5</span><span class="text-base-content/35"> / 10</span>
</x-table.td>
<x-table.td><x-badge color="success" appearance="soft" size="sm">Active</x-badge></x-table.td>
<x-table.td align="right"><x-toggle size="sm" :checked="true" /></x-table.td>
<x-table.td action><x-button size="xs" appearance="ghost">Edit</x-button></x-table.td>
</tr>
<tr>
<x-table.td><span class="font-medium">Northwind Sales</span></x-table.td>
<x-table.td numeric muted>7</x-table.td>
<x-table.td numeric>
<span class="font-medium">7</span><span class="text-base-content/35"> / 7</span>
</x-table.td>
<x-table.td><x-badge color="warning" appearance="soft" size="sm">Seats full</x-badge></x-table.td>
<x-table.td align="right"><x-toggle size="sm" /></x-table.td>
<x-table.td action><x-button size="xs" appearance="ghost">Edit</x-button></x-table.td>
</tr>
<tr>
<x-table.td><span class="font-medium">Globex Support</span></x-table.td>
<x-table.td numeric muted>42</x-table.td>
<x-table.td numeric>
<span class="font-medium">31</span><span class="text-base-content/35"> / 50</span>
</x-table.td>
<x-table.td><x-badge color="success" appearance="soft" size="sm">Active</x-badge></x-table.td>
<x-table.td align="right"><x-toggle size="sm" :checked="true" /></x-table.td>
<x-table.td action><x-button size="xs" appearance="ghost">Edit</x-button></x-table.td>
</tr>
<tr>
<x-table.td><span class="font-medium">Initech Research</span></x-table.td>
<x-table.td numeric muted>3</x-table.td>
<x-table.td numeric>
<span class="font-medium">1</span><span class="text-base-content/35"> / 5</span>
</x-table.td>
<x-table.td><x-badge color="neutral" appearance="soft" size="sm">Trial</x-badge></x-table.td>
<x-table.td align="right"><x-toggle size="sm" /></x-table.td>
<x-table.td action><x-button size="xs" appearance="ghost">Edit</x-button></x-table.td>
</tr>
</x-table>
<x-table>
<x-slot:head>
<x-table.th>Workspace</x-table.th>
<x-table.th numeric>Members</x-table.th>
<x-table.th action />
</x-slot:head>
@forelse ($workspaces as $workspace)
<tr wire:key="workspace-{{ $workspace->id }}">
<x-table.td>{{ $workspace->name }}</x-table.td>
<x-table.td numeric>{{ $workspace->members_count }}</x-table.td>
<x-table.td action>
<x-button size="xs" appearance="ghost">Edit</x-button>
</x-table.td>
</tr>
@empty
<x-table.empty>No workspaces yet</x-table.empty>
@endforelse
</x-table>
空状態 — <x-table.empty> は全カラムに自動で colspan される
| Workspace | Members | Seats | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
No workspaces yet Create one to start inviting members. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<x-table>
<x-slot:head>
<x-table.th>Workspace</x-table.th>
<x-table.th numeric>Members</x-table.th>
<x-table.th numeric>Seats</x-table.th>
<x-table.th action />
</x-slot:head>
<x-table.empty>
<p class="text-sm text-base-content/55">No workspaces yet</p>
<p class="mt-2xs text-xs text-base-content/35">Create one to start inviting members.</p>
</x-table.empty>
</x-table>
行の密度 — size="sm" / size="lg"
size="sm"
| Workspace | Members |
|---|---|
| Acme Marketing | 18 |
| Northwind Sales | 7 |
| Globex Support | 42 |
size="lg"
| Workspace | Members |
|---|---|
| Acme Marketing | 18 |
| Northwind Sales | 7 |
| Globex Support | 42 |
<div class="grid gap-lg md:grid-cols-2 md:items-start">
<div class="space-y-xs">
<p class="text-xs font-medium text-base-content/50">size="sm"</p>
<x-table size="sm">
<x-slot:head>
<x-table.th>Workspace</x-table.th>
<x-table.th numeric>Members</x-table.th>
</x-slot:head>
<tr>
<x-table.td>Acme Marketing</x-table.td>
<x-table.td numeric>18</x-table.td>
</tr>
<tr>
<x-table.td>Northwind Sales</x-table.td>
<x-table.td numeric>7</x-table.td>
</tr>
<tr>
<x-table.td>Globex Support</x-table.td>
<x-table.td numeric>42</x-table.td>
</tr>
</x-table>
</div>
<div class="space-y-xs">
<p class="text-xs font-medium text-base-content/50">size="lg"</p>
<x-table size="lg">
<x-slot:head>
<x-table.th>Workspace</x-table.th>
<x-table.th numeric>Members</x-table.th>
</x-slot:head>
<tr>
<x-table.td>Acme Marketing</x-table.td>
<x-table.td numeric>18</x-table.td>
</tr>
<tr>
<x-table.td>Northwind Sales</x-table.td>
<x-table.td numeric>7</x-table.td>
</tr>
<tr>
<x-table.td>Globex Support</x-table.td>
<x-table.td numeric>42</x-table.td>
</tr>
</x-table>
</div>
</div>
既存のカードの中に入れる — <x-card :padding="false"> + <x-table :card="false">
Invoices this month
3 records| Invoice | Customer | Amount | |
|---|---|---|---|
| INV-2041 | Acme Marketing | $1,280.00 | |
| INV-2042 | Northwind Sales | $540.00 | |
| INV-2043 | Globex Support | $12,900.00 |
<x-card :padding="false">
<x-slot:header>
<div class="p-lg flex items-baseline justify-between">
<h2 class="text-sm font-semibold">Invoices this month</h2>
<span class="text-xs text-base-content/40">3 records</span>
</div>
</x-slot:header>
<x-table :card="false">
<x-slot:head>
<x-table.th>Invoice</x-table.th>
<x-table.th>Customer</x-table.th>
<x-table.th numeric>Amount</x-table.th>
<x-table.th action />
</x-slot:head>
<tr>
<x-table.td><span class="font-medium">INV-2041</span></x-table.td>
<x-table.td muted>Acme Marketing</x-table.td>
<x-table.td numeric>$1,280.00</x-table.td>
<x-table.td action><x-button size="xs" appearance="ghost">View</x-button></x-table.td>
</tr>
<tr>
<x-table.td><span class="font-medium">INV-2042</span></x-table.td>
<x-table.td muted>Northwind Sales</x-table.td>
<x-table.td numeric>$540.00</x-table.td>
<x-table.td action><x-button size="xs" appearance="ghost">View</x-button></x-table.td>
</tr>
<tr>
<x-table.td><span class="font-medium">INV-2043</span></x-table.td>
<x-table.td muted>Globex Support</x-table.td>
<x-table.td numeric>$12,900.00</x-table.td>
<x-table.td action><x-button size="xs" appearance="ghost">View</x-button></x-table.td>
</tr>
</x-table>
</x-card>
行のホバーを止める — :hover="false"
| Region | Signups |
|---|---|
| Europe | 1,204 |
| Asia Pacific | 982 |
<x-table :hover="false">
<x-slot:head>
<x-table.th>Region</x-table.th>
<x-table.th numeric>Signups</x-table.th>
</x-slot:head>
<tr>
<x-table.td>Europe</x-table.td>
<x-table.td numeric>1,204</x-table.td>
</tr>
<tr>
<x-table.td>Asia Pacific</x-table.td>
<x-table.td numeric>982</x-table.td>
</tr>
</x-table>
Props
| Prop | デフォルト | 説明 |
|---|---|---|
size | md | <x-table> の行密度。sm(コンパクト・本文 text-xs)/ md / lg(ゆったり)。各セルへ自動で伝播するので、セル側に指定しない。 |
card | true | true = table 自身がカード面(枠線・角丸・bg-base-100)を持つ。既存の <x-card :padding="false"> の中に入れる時だけ :card="false"。 |
hover | true | 本文行のホバー強調。空状態の行は常に対象外。 |
numeric | false | th / td で右寄せ。td では tabular-nums も付き、桁が縦に揃う。件数・金額・日付向け。 |
action | false | th / td で右寄せ。td では whitespace-nowrap も付く。末尾のボタン列に使う(ヘッダは <x-table.th action /> )。 |
muted | false | td 専用 — 文字を薄く(text-base-content/60)する。主役の列と競合させたくない補助的な数値に。 |
align | null | left / center / right の明示指定。numeric・action より優先される。 |
- 本文の行は素の <tr>。区切り線・ホバー・最終行の枠線処理は親から注入されるので、ループでもラッパーコンポーネントが要らない。
- size は @aware で親から各セルへ流れる。セルに size を書いても効かない。
- シェル自体が横スクロールする。フェードとスクロールボタンも欲しい時だけ <x-table-scroll> で包む。