412 lines
12 KiB
Markdown
412 lines
12 KiB
Markdown
# Table
|
||
|
||
Data table component with sorting, pagination, zebra stripes, pin rows, and custom cell rendering.
|
||
|
||
## Tag
|
||
|
||
`Table`
|
||
|
||
## Props
|
||
|
||
| Prop | Type | Default | Description |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `items` | `Array \| Signal<Array>` | `[]` | Data array to display |
|
||
| `columns` | `Array<Column>` | `[]` | Column definitions |
|
||
| `keyFn` | `function` | `(item, idx) => idx` | Unique key function for rows |
|
||
| `zebra` | `boolean \| Signal<boolean>` | `false` | Enable zebra striping |
|
||
| `pinRows` | `boolean \| Signal<boolean>` | `false` | Pin header rows on scroll |
|
||
| `empty` | `string \| VNode` | `'No data'` | Content to show when no data |
|
||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||
|
||
### Column Structure
|
||
|
||
| Property | Type | Description |
|
||
| :--- | :--- | :--- |
|
||
| `label` | `string` | Column header text |
|
||
| `key` | `string` | Property key to display from item |
|
||
| `render` | `function(item, index)` | Custom render function for cell content |
|
||
| `class` | `string` | Additional CSS classes for column cells |
|
||
| `footer` | `string \| VNode` | Footer content for the column |
|
||
|
||
## Styling
|
||
|
||
Table supports all **daisyUI Table classes**:
|
||
|
||
| Category | Keywords | Description |
|
||
| :--- | :--- | :--- |
|
||
| Base | `table` | Base table styling |
|
||
| Variant | `table-zebra` | Zebra striping |
|
||
| Size | `table-xs`, `table-sm`, `table-md`, `table-lg`, `table-xl` | Table scale |
|
||
| Feature | `table-pin-rows`, `table-pin-cols` | Pin headers/columns |
|
||
|
||
> For further details, check the [daisyUI Table Documentation](https://daisyui.com/components/table) – Full reference for CSS classes.
|
||
|
||
## Live Examples
|
||
|
||
### Basic Table
|
||
|
||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const BasicDemo = () => {
|
||
const users = [
|
||
{ id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
|
||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
|
||
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' }
|
||
];
|
||
|
||
return Table({
|
||
items: users,
|
||
columns: [
|
||
{ label: 'ID', key: 'id' },
|
||
{ label: 'Name', key: 'name' },
|
||
{ label: 'Email', key: 'email' },
|
||
{ label: 'Role', key: 'role' }
|
||
]
|
||
});
|
||
};
|
||
Mount(BasicDemo, '#demo-basic');
|
||
```
|
||
|
||
### With Zebra Stripes
|
||
|
||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||
<div id="demo-zebra" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const ZebraDemo = () => {
|
||
const products = [
|
||
{ id: 1, name: 'Laptop', price: '$999', stock: 15 },
|
||
{ id: 2, name: 'Mouse', price: '$29', stock: 42 },
|
||
{ id: 3, name: 'Keyboard', price: '$79', stock: 28 },
|
||
{ id: 4, name: 'Monitor', price: '$299', stock: 12 }
|
||
];
|
||
|
||
return Table({
|
||
items: products,
|
||
columns: [
|
||
{ label: 'Product', key: 'name' },
|
||
{ label: 'Price', key: 'price' },
|
||
{ label: 'Stock', key: 'stock' }
|
||
],
|
||
zebra: true
|
||
});
|
||
};
|
||
Mount(ZebraDemo, '#demo-zebra');
|
||
```
|
||
|
||
### With Custom Cell Rendering
|
||
|
||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||
<div id="demo-custom" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const CustomDemo = () => {
|
||
const orders = [
|
||
{ id: 101, customer: 'Alice', amount: 250, status: 'completed' },
|
||
{ id: 102, customer: 'Bob', amount: 89, status: 'pending' },
|
||
{ id: 103, customer: 'Charlie', amount: 450, status: 'shipped' }
|
||
];
|
||
|
||
return Table({
|
||
items: orders,
|
||
columns: [
|
||
{ label: 'Order ID', key: 'id' },
|
||
{ label: 'Customer', key: 'customer' },
|
||
{
|
||
label: 'Amount',
|
||
key: 'amount',
|
||
render: (item) => `$${item.amount}`
|
||
},
|
||
{
|
||
label: 'Status',
|
||
key: 'status',
|
||
render: (item) => {
|
||
const statusClass = {
|
||
completed: 'badge badge-success',
|
||
pending: 'badge badge-warning',
|
||
shipped: 'badge badge-info'
|
||
};
|
||
return Span({ class: statusClass[item.status] }, item.status);
|
||
}
|
||
}
|
||
],
|
||
zebra: true
|
||
});
|
||
};
|
||
Mount(CustomDemo, '#demo-custom');
|
||
```
|
||
|
||
### With Footers
|
||
|
||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||
<div id="demo-footer" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const FooterDemo = () => {
|
||
const sales = [
|
||
{ month: 'January', revenue: 12500, expenses: 8900 },
|
||
{ month: 'February', revenue: 14200, expenses: 9200 },
|
||
{ month: 'March', revenue: 16800, expenses: 10100 }
|
||
];
|
||
|
||
const totalRevenue = sales.reduce((sum, item) => sum + item.revenue, 0);
|
||
const totalExpenses = sales.reduce((sum, item) => sum + item.expenses, 0);
|
||
|
||
return Table({
|
||
items: sales,
|
||
columns: [
|
||
{ label: 'Month', key: 'month' },
|
||
{
|
||
label: 'Revenue',
|
||
key: 'revenue',
|
||
render: (item) => `$${item.revenue.toLocaleString()}`,
|
||
footer: `Total: $${totalRevenue.toLocaleString()}`
|
||
},
|
||
{
|
||
label: 'Expenses',
|
||
key: 'expenses',
|
||
render: (item) => `$${item.expenses.toLocaleString()}`,
|
||
footer: `Total: $${totalExpenses.toLocaleString()}`
|
||
},
|
||
{
|
||
label: 'Profit',
|
||
render: (item) => `$${(item.revenue - item.expenses).toLocaleString()}`,
|
||
footer: `$${(totalRevenue - totalExpenses).toLocaleString()}`
|
||
}
|
||
],
|
||
zebra: true
|
||
});
|
||
};
|
||
Mount(FooterDemo, '#demo-footer');
|
||
```
|
||
|
||
### Empty State
|
||
|
||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||
<div id="demo-empty" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const EmptyDemo = () => {
|
||
const emptyList = [];
|
||
|
||
return Table({
|
||
items: emptyList,
|
||
columns: [
|
||
{ label: 'ID', key: 'id' },
|
||
{ label: 'Name', key: 'name' }
|
||
],
|
||
empty: Div({ class: 'flex flex-col items-center gap-2 p-4' }, [
|
||
Span({ class: 'text-2xl' }, '📭'),
|
||
Span({}, 'No records found')
|
||
])
|
||
});
|
||
};
|
||
Mount(EmptyDemo, '#demo-empty');
|
||
```
|
||
|
||
### Reactive Data
|
||
|
||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||
<div id="demo-reactive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const ReactiveDemo = () => {
|
||
const filter = $('all');
|
||
const tasks = $([
|
||
{ id: 1, title: 'Complete documentation', completed: true },
|
||
{ id: 2, title: 'Review pull requests', completed: false },
|
||
{ id: 3, title: 'Deploy to production', completed: false },
|
||
{ id: 4, title: 'Update dependencies', completed: true }
|
||
]);
|
||
|
||
const filteredTasks = () => {
|
||
if (filter() === 'completed') {
|
||
return tasks().filter(t => t.completed);
|
||
} else if (filter() === 'pending') {
|
||
return tasks().filter(t => !t.completed);
|
||
}
|
||
return tasks();
|
||
};
|
||
|
||
const addTask = () => {
|
||
const newId = Math.max(...tasks().map(t => t.id), 0) + 1;
|
||
tasks([...tasks(), { id: newId, title: `Task ${newId}`, completed: false }]);
|
||
};
|
||
|
||
return Div({ class: 'flex flex-col gap-4' }, [
|
||
Div({ class: 'flex gap-2' }, [
|
||
Button({
|
||
class: 'btn btn-sm',
|
||
onclick: () => filter('all')
|
||
}, 'All'),
|
||
Button({
|
||
class: 'btn btn-sm',
|
||
onclick: () => filter('completed')
|
||
}, 'Completed'),
|
||
Button({
|
||
class: 'btn btn-sm',
|
||
onclick: () => filter('pending')
|
||
}, 'Pending'),
|
||
Button({
|
||
class: 'btn btn-sm btn-primary',
|
||
onclick: addTask
|
||
}, 'Add Task')
|
||
]),
|
||
Table({
|
||
items: filteredTasks,
|
||
columns: [
|
||
{ label: 'ID', key: 'id' },
|
||
{ label: 'Title', key: 'title' },
|
||
{
|
||
label: 'Status',
|
||
render: (item) => item.completed
|
||
? Span({ class: 'badge badge-success' }, '✓ Done')
|
||
: Span({ class: 'badge badge-warning' }, '○ Pending')
|
||
}
|
||
],
|
||
zebra: true
|
||
})
|
||
]);
|
||
};
|
||
Mount(ReactiveDemo, '#demo-reactive');
|
||
```
|
||
|
||
### With Actions
|
||
|
||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||
<div id="demo-actions" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const ActionsDemo = () => {
|
||
const users = $([
|
||
{ id: 1, name: 'John Doe', email: 'john@example.com', active: true },
|
||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', active: false },
|
||
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', active: true }
|
||
]);
|
||
|
||
const deleteUser = (id) => {
|
||
users(users().filter(u => u.id !== id));
|
||
Toast('User deleted', 'alert-info', 2000);
|
||
};
|
||
|
||
const toggleActive = (id) => {
|
||
users(users().map(u =>
|
||
u.id === id ? { ...u, active: !u.active } : u
|
||
));
|
||
};
|
||
|
||
return Table({
|
||
items: users,
|
||
columns: [
|
||
{ label: 'ID', key: 'id' },
|
||
{ label: 'Name', key: 'name' },
|
||
{ label: 'Email', key: 'email' },
|
||
{
|
||
label: 'Status',
|
||
render: (item) => item.active
|
||
? Span({ class: 'badge badge-success' }, 'Active')
|
||
: Span({ class: 'badge badge-ghost' }, 'Inactive')
|
||
},
|
||
{
|
||
label: 'Actions',
|
||
render: (item) => Div({ class: 'flex gap-1' }, [
|
||
Button({
|
||
class: 'btn btn-xs btn-ghost',
|
||
onclick: () => toggleActive(item.id)
|
||
}, item.active ? 'Deactivate' : 'Activate'),
|
||
Button({
|
||
class: 'btn btn-xs btn-error',
|
||
onclick: () => deleteUser(item.id)
|
||
}, 'Delete')
|
||
])
|
||
}
|
||
],
|
||
zebra: true
|
||
});
|
||
};
|
||
Mount(ActionsDemo, '#demo-actions');
|
||
```
|
||
|
||
### All Variants
|
||
|
||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||
<div class="card-body">
|
||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-6"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const VariantsDemo = () => {
|
||
const data = [
|
||
{ id: 1, name: 'Item 1', value: 100 },
|
||
{ id: 2, name: 'Item 2', value: 200 },
|
||
{ id: 3, name: 'Item 3', value: 300 }
|
||
];
|
||
|
||
return Div({ class: 'flex flex-col gap-6' }, [
|
||
Div({ class: 'text-sm font-bold' }, 'Default Table'),
|
||
Table({
|
||
items: data,
|
||
columns: [
|
||
{ label: 'ID', key: 'id' },
|
||
{ label: 'Name', key: 'name' },
|
||
{ label: 'Value', key: 'value' }
|
||
]
|
||
}),
|
||
|
||
Div({ class: 'text-sm font-bold mt-4' }, 'Zebra Stripes'),
|
||
Table({
|
||
items: data,
|
||
columns: [
|
||
{ label: 'ID', key: 'id' },
|
||
{ label: 'Name', key: 'name' },
|
||
{ label: 'Value', key: 'value' }
|
||
],
|
||
zebra: true
|
||
}),
|
||
|
||
Div({ class: 'text-sm font-bold mt-4' }, 'Compact Table'),
|
||
Table({
|
||
items: data,
|
||
columns: [
|
||
{ label: 'ID', key: 'id' },
|
||
{ label: 'Name', key: 'name' },
|
||
{ label: 'Value', key: 'value' }
|
||
],
|
||
class: 'table-compact'
|
||
})
|
||
]);
|
||
};
|
||
Mount(VariantsDemo, '#demo-variants');
|
||
``` |