# TableBody

## Description

A semantic container component that wraps the main content section of a table. TableBody provides proper HTML structure for table data rows and ensures accessibility compliance by creating the `<tbody>` element that groups the table's primary content together.

## Aliases

- TableBody
- TBody
- TableContent
- BodySection
- TableDataGroup

## Props Breakdown

**Extends:** `HTMLAttributes<HTMLTableSectionElement>`

| Prop | Type | Default | Required | Description |
|------|------|---------|----------|-------------|
| `children` | `ReactNode` | - | Yes | Table body content, typically TableRow components containing TableCell elements |

Plus all standard HTML table section attributes (id, className, style, role, etc.).

## Examples

### Basic Table Body
```tsx
import { Table, TableHeader, TableBody, TableRow, TableHeaderCell, TableCell } from '@delightui/components';

function BasicBodyExample() {
  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>
      <TableHeader>
        <TableRow>
          <TableHeaderCell>Name</TableHeaderCell>
          <TableHeaderCell>Email</TableHeaderCell>
          <TableHeaderCell>Role</TableHeaderCell>
        </TableRow>
      </TableHeader>
      <TableBody>
        {users.map(user => (
          <TableRow key={user.id}>
            <TableCell>{user.name}</TableCell>
            <TableCell>{user.email}</TableCell>
            <TableCell>{user.role}</TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}
```

### Dynamic Content Loading
```tsx
function LoadingBodyExample() {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setIsLoading(true);
        // Simulate API call
        await new Promise(resolve => setTimeout(resolve, 2000));
        setData([
          { id: 1, product: 'Laptop', category: 'Electronics', price: 999.99, stock: 15 },
          { id: 2, product: 'Desk Chair', category: 'Furniture', price: 299.99, stock: 8 },
          { id: 3, product: 'Coffee Mug', category: 'Kitchen', price: 12.99, stock: 50 }
        ]);
      } catch (err) {
        setError('Failed to load data');
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();
  }, []);

  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHeaderCell>Product</TableHeaderCell>
          <TableHeaderCell>Category</TableHeaderCell>
          <TableHeaderCell>Price</TableHeaderCell>
          <TableHeaderCell>Stock</TableHeaderCell>
        </TableRow>
      </TableHeader>
      <TableBody>
        {isLoading ? (
          Array.from({ length: 3 }).map((_, index) => (
            <TableRow key={`loading-${index}`}>
              <TableCell>
                <div className="skeleton-loader skeleton-text"></div>
              </TableCell>
              <TableCell>
                <div className="skeleton-loader skeleton-text-small"></div>
              </TableCell>
              <TableCell>
                <div className="skeleton-loader skeleton-text-small"></div>
              </TableCell>
              <TableCell>
                <div className="skeleton-loader skeleton-text-small"></div>
              </TableCell>
            </TableRow>
          ))
        ) : error ? (
          <TableRow>
            <TableCell colSpan={4} className="error-state">
              <div className="error-message">
                <Icon icon="Error" className="error-icon" />
                <Text type="BodyMedium">{error}</Text>
                <Button size="Small" onClick={() => window.location.reload()}>
                  Retry
                </Button>
              </div>
            </TableCell>
          </TableRow>
        ) : data.length === 0 ? (
          <TableRow>
            <TableCell colSpan={4} className="empty-state">
              <div className="empty-message">
                <Icon icon="Inventory" className="empty-icon" />
                <Text type="BodyMedium">No products found</Text>
                <Text type="BodySmall">Add your first product to get started</Text>
              </div>
            </TableCell>
          </TableRow>
        ) : (
          data.map(item => (
            <TableRow key={item.id}>
              <TableCell>{item.product}</TableCell>
              <TableCell>{item.category}</TableCell>
              <TableCell>${item.price}</TableCell>
              <TableCell>{item.stock}</TableCell>
            </TableRow>
          ))
        )}
      </TableBody>
    </Table>
  );
}
```

### Striped Rows Body
```tsx
function StripedBodyExample() {
  const transactions = [
    { id: 'T001', date: '2023-10-15', description: 'Online Purchase', amount: -89.99, status: 'Completed' },
    { id: 'T002', date: '2023-10-14', description: 'Salary Deposit', amount: 3500.00, status: 'Completed' },
    { id: 'T003', date: '2023-10-13', description: 'ATM Withdrawal', amount: -100.00, status: 'Completed' },
    { id: 'T004', date: '2023-10-12', description: 'Restaurant', amount: -45.67, status: 'Pending' },
    { id: 'T005', date: '2023-10-11', description: 'Gas Station', amount: -55.20, status: 'Completed' }
  ];

  return (
    <Table className="striped-table">
      <TableHeader>
        <TableRow>
          <TableHeaderCell>Transaction ID</TableHeaderCell>
          <TableHeaderCell>Date</TableHeaderCell>
          <TableHeaderCell>Description</TableHeaderCell>
          <TableHeaderCell>Amount</TableHeaderCell>
          <TableHeaderCell>Status</TableHeaderCell>
        </TableRow>
      </TableHeader>
      <TableBody className="striped-body">
        {transactions.map((transaction, index) => (
          <TableRow key={transaction.id} className={index % 2 === 0 ? 'even-row' : 'odd-row'}>
            <TableCell>{transaction.id}</TableCell>
            <TableCell>{transaction.date}</TableCell>
            <TableCell>{transaction.description}</TableCell>
            <TableCell className={transaction.amount > 0 ? 'positive-amount' : 'negative-amount'}>
              ${Math.abs(transaction.amount).toFixed(2)}
            </TableCell>
            <TableCell>
              <Chip style={transaction.status === 'Completed' ? 'Success' : 'Warning'}>
                {transaction.status}
              </Chip>
            </TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}
```

### Grouped Data Body
```tsx
function GroupedBodyExample() {
  const groupedData = {
    'Engineering': [
      { id: 1, name: 'Alice Johnson', role: 'Senior Developer', salary: 95000 },
      { id: 2, name: 'Bob Smith', role: 'Frontend Developer', salary: 75000 },
      { id: 3, name: 'Carol Wilson', role: 'Backend Developer', salary: 80000 }
    ],
    'Design': [
      { id: 4, name: 'David Brown', role: 'UI Designer', salary: 70000 },
      { id: 5, name: 'Eve Davis', role: 'UX Designer', salary: 75000 }
    ],
    'Marketing': [
      { id: 6, name: 'Frank Miller', role: 'Marketing Manager', salary: 85000 },
      { id: 7, name: 'Grace Taylor', role: 'Content Specialist', salary: 55000 }
    ]
  };

  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHeaderCell>Employee</TableHeaderCell>
          <TableHeaderCell>Role</TableHeaderCell>
          <TableHeaderCell>Salary</TableHeaderCell>
          <TableHeaderCell>Actions</TableHeaderCell>
        </TableRow>
      </TableHeader>
      <TableBody className="grouped-body">
        {Object.entries(groupedData).map(([department, employees]) => (
          <React.Fragment key={department}>
            <TableRow className="group-header-row">
              <TableCell colSpan={4} className="group-header">
                <div className="group-title">
                  <Icon icon="Group" size="Small" />
                  <Text type="BodyMedium">{department} Department</Text>
                  <Chip size="Small">{employees.length} employees</Chip>
                </div>
              </TableCell>
            </TableRow>
            {employees.map(employee => (
              <TableRow key={employee.id} className="group-item-row">
                <TableCell>{employee.name}</TableCell>
                <TableCell>{employee.role}</TableCell>
                <TableCell>${employee.salary.toLocaleString()}</TableCell>
                <TableCell>
                  <Button size="Small" type="Ghost">Edit</Button>
                </TableCell>
              </TableRow>
            ))}
          </React.Fragment>
        ))}
      </TableBody>
    </Table>
  );
}
```

### Interactive Row Selection
```tsx
function SelectableBodyExample() {
  const [selectedRows, setSelectedRows] = useState(new Set());
  const [hoveredRow, setHoveredRow] = useState(null);

  const data = [
    { id: 1, task: 'Update documentation', assignee: 'John', priority: 'High', status: 'In Progress' },
    { id: 2, task: 'Fix login bug', assignee: 'Jane', priority: 'Critical', status: 'Todo' },
    { id: 3, task: 'Design new feature', assignee: 'Bob', priority: 'Medium', status: 'Review' },
    { id: 4, task: 'Write unit tests', assignee: 'Alice', priority: 'Low', status: 'Done' }
  ];

  const handleRowSelect = (id, selected) => {
    const newSelected = new Set(selectedRows);
    if (selected) {
      newSelected.add(id);
    } else {
      newSelected.delete(id);
    }
    setSelectedRows(newSelected);
  };

  const isRowSelected = (id) => selectedRows.has(id);

  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHeaderCell>
            <Checkbox 
              checked={selectedRows.size === data.length}
              indeterminate={selectedRows.size > 0 && selectedRows.size < data.length}
              onChange={(e) => {
                if (e.target.checked) {
                  setSelectedRows(new Set(data.map(item => item.id)));
                } else {
                  setSelectedRows(new Set());
                }
              }}
            />
          </TableHeaderCell>
          <TableHeaderCell>Task</TableHeaderCell>
          <TableHeaderCell>Assignee</TableHeaderCell>
          <TableHeaderCell>Priority</TableHeaderCell>
          <TableHeaderCell>Status</TableHeaderCell>
        </TableRow>
      </TableHeader>
      <TableBody className="selectable-body">
        {data.map(item => (
          <TableRow 
            key={item.id}
            className={`
              ${isRowSelected(item.id) ? 'selected-row' : ''}
              ${hoveredRow === item.id ? 'hovered-row' : ''}
            `}
            onMouseEnter={() => setHoveredRow(item.id)}
            onMouseLeave={() => setHoveredRow(null)}
          >
            <TableCell>
              <Checkbox 
                checked={isRowSelected(item.id)}
                onChange={(e) => handleRowSelect(item.id, e.target.checked)}
              />
            </TableCell>
            <TableCell>{item.task}</TableCell>
            <TableCell>{item.assignee}</TableCell>
            <TableCell>
              <Chip 
                style={
                  item.priority === 'Critical' ? 'Error' :
                  item.priority === 'High' ? 'Warning' :
                  item.priority === 'Medium' ? 'Default' : 'Success'
                }
              >
                {item.priority}
              </Chip>
            </TableCell>
            <TableCell>
              <Chip 
                style={
                  item.status === 'Done' ? 'Success' :
                  item.status === 'In Progress' ? 'Warning' : 'Default'
                }
              >
                {item.status}
              </Chip>
            </TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}
```

### Expandable Rows Body
```tsx
function ExpandableBodyExample() {
  const [expandedRows, setExpandedRows] = useState(new Set());

  const orders = [
    {
      id: 'ORD-001',
      customer: 'John Doe',
      date: '2023-10-15',
      total: 299.99,
      status: 'Delivered',
      items: [
        { name: 'Wireless Headphones', quantity: 1, price: 199.99 },
        { name: 'Phone Case', quantity: 2, price: 50.00 }
      ]
    },
    {
      id: 'ORD-002',
      customer: 'Jane Smith',
      date: '2023-10-14',
      total: 159.97,
      status: 'Shipped',
      items: [
        { name: 'Bluetooth Speaker', quantity: 1, price: 89.99 },
        { name: 'USB Cable', quantity: 1, price: 19.99 },
        { name: 'Wall Charger', quantity: 2, price: 25.00 }
      ]
    }
  ];

  const toggleExpanded = (orderId) => {
    const newExpanded = new Set(expandedRows);
    if (newExpanded.has(orderId)) {
      newExpanded.delete(orderId);
    } else {
      newExpanded.add(orderId);
    }
    setExpandedRows(newExpanded);
  };

  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHeaderCell></TableHeaderCell>
          <TableHeaderCell>Order ID</TableHeaderCell>
          <TableHeaderCell>Customer</TableHeaderCell>
          <TableHeaderCell>Date</TableHeaderCell>
          <TableHeaderCell>Total</TableHeaderCell>
          <TableHeaderCell>Status</TableHeaderCell>
        </TableRow>
      </TableHeader>
      <TableBody className="expandable-body">
        {orders.map(order => (
          <React.Fragment key={order.id}>
            <TableRow className="main-row">
              <TableCell>
                <Button 
                  size="Small" 
                  type="Ghost"
                  onClick={() => toggleExpanded(order.id)}
                >
                  <Icon 
                    icon={expandedRows.has(order.id) ? 'ExpandLess' : 'ExpandMore'} 
                    size="Small" 
                  />
                </Button>
              </TableCell>
              <TableCell>{order.id}</TableCell>
              <TableCell>{order.customer}</TableCell>
              <TableCell>{order.date}</TableCell>
              <TableCell>${order.total}</TableCell>
              <TableCell>
                <Chip style={order.status === 'Delivered' ? 'Success' : 'Warning'}>
                  {order.status}
                </Chip>
              </TableCell>
            </TableRow>
            {expandedRows.has(order.id) && (
              <TableRow className="expanded-row">
                <TableCell colSpan={6}>
                  <div className="expanded-content">
                    <Text type="BodyMedium">Order Items</Text>
                    <div className="items-list">
                      {order.items.map((item, index) => (
                        <div key={index} className="order-item">
                          <Text type="BodySmall">{item.name}</Text>
                          <Text type="BodySmall">Qty: {item.quantity}</Text>
                          <Text type="BodySmall">${item.price}</Text>
                        </div>
                      ))}
                    </div>
                  </div>
                </TableCell>
              </TableRow>
            )}
          </React.Fragment>
        ))}
      </TableBody>
    </Table>
  );
}
```

### Infinite Scroll Body
```tsx
function InfiniteScrollBodyExample() {
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);
  
  const loadMoreData = useCallback(async () => {
    if (loading || !hasMore) return;
    
    setLoading(true);
    // Simulate API call
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    const newItems = Array.from({ length: 20 }, (_, i) => ({
      id: data.length + i + 1,
      name: `Item ${data.length + i + 1}`,
      value: Math.floor(Math.random() * 1000),
      status: ['Active', 'Inactive', 'Pending'][Math.floor(Math.random() * 3)]
    }));
    
    setData(prev => [...prev, ...newItems]);
    setLoading(false);
    
    // Stop loading after 100 items
    if (data.length + newItems.length >= 100) {
      setHasMore(false);
    }
  }, [data.length, loading, hasMore]);

  useEffect(() => {
    loadMoreData();
  }, []);

  return (
    <div className="infinite-scroll-container" style={{ height: '400px', overflow: 'auto' }}>
      <Table>
        <TableHeader>
          <TableRow>
            <TableHeaderCell>Name</TableHeaderCell>
            <TableHeaderCell>Value</TableHeaderCell>
            <TableHeaderCell>Status</TableHeaderCell>
          </TableRow>
        </TableHeader>
        <TableBody className="infinite-scroll-body">
          {data.map(item => (
            <TableRow key={item.id}>
              <TableCell>{item.name}</TableCell>
              <TableCell>{item.value}</TableCell>
              <TableCell>
                <Chip style={item.status === 'Active' ? 'Success' : 'Default'}>
                  {item.status}
                </Chip>
              </TableCell>
            </TableRow>
          ))}
          {loading && (
            <TableRow>
              <TableCell colSpan={3} className="loading-row">
                <div className="loading-indicator">
                  <Spinner size="Small" />
                  <Text type="BodySmall">Loading more items...</Text>
                </div>
              </TableCell>
            </TableRow>
          )}
          {!hasMore && data.length > 0 && (
            <TableRow>
              <TableCell colSpan={3} className="end-row">
                <Text type="BodySmall">No more items to load</Text>
              </TableCell>
            </TableRow>
          )}
        </TableBody>
      </Table>
    </div>
  );
}
```

### Drag and Drop Body
```tsx
function DragDropBodyExample() {
  const [items, setItems] = useState([
    { id: 1, task: 'High priority task', priority: 1 },
    { id: 2, task: 'Medium priority task', priority: 2 },
    { id: 3, task: 'Low priority task', priority: 3 },
    { id: 4, task: 'Another task', priority: 4 }
  ]);

  const [draggedItem, setDraggedItem] = useState(null);

  const handleDragStart = (e, item) => {
    setDraggedItem(item);
    e.dataTransfer.effectAllowed = 'move';
  };

  const handleDragOver = (e) => {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
  };

  const handleDrop = (e, targetItem) => {
    e.preventDefault();
    if (!draggedItem || draggedItem.id === targetItem.id) return;

    const newItems = [...items];
    const draggedIndex = newItems.findIndex(item => item.id === draggedItem.id);
    const targetIndex = newItems.findIndex(item => item.id === targetItem.id);

    // Remove dragged item and insert at target position
    const [draggedItemData] = newItems.splice(draggedIndex, 1);
    newItems.splice(targetIndex, 0, draggedItemData);

    // Update priority values
    const updatedItems = newItems.map((item, index) => ({
      ...item,
      priority: index + 1
    }));

    setItems(updatedItems);
    setDraggedItem(null);
  };

  return (
    <Table>
      <TableHeader>
        <TableRow>
          <TableHeaderCell>Priority</TableHeaderCell>
          <TableHeaderCell>Task</TableHeaderCell>
          <TableHeaderCell>Actions</TableHeaderCell>
        </TableRow>
      </TableHeader>
      <TableBody className="draggable-body">
        {items.map(item => (
          <TableRow 
            key={item.id}
            draggable
            onDragStart={(e) => handleDragStart(e, item)}
            onDragOver={handleDragOver}
            onDrop={(e) => handleDrop(e, item)}
            className={`draggable-row ${draggedItem?.id === item.id ? 'dragging' : ''}`}
          >
            <TableCell>
              <div className="priority-cell">
                <Icon icon="DragIndicator" className="drag-handle" />
                <Text type="BodyMedium">{item.priority}</Text>
              </div>
            </TableCell>
            <TableCell>{item.task}</TableCell>
            <TableCell>
              <Button size="Small" type="Ghost">Edit</Button>
            </TableCell>
          </TableRow>
        ))}
      </TableBody>
    </Table>
  );
}
```

### Virtual Scrolling Body
```tsx
function VirtualScrollBodyExample() {
  const [data] = useState(
    Array.from({ length: 10000 }, (_, i) => ({
      id: i + 1,
      name: `User ${i + 1}`,
      email: `user${i + 1}@example.com`,
      score: Math.floor(Math.random() * 1000)
    }))
  );

  const [scrollTop, setScrollTop] = useState(0);
  const itemHeight = 50;
  const containerHeight = 400;
  const visibleCount = Math.ceil(containerHeight / itemHeight);
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = Math.min(startIndex + visibleCount + 5, data.length);
  const visibleItems = data.slice(startIndex, endIndex);

  const handleScroll = (e) => {
    setScrollTop(e.target.scrollTop);
  };

  return (
    <div 
      className="virtual-scroll-container"
      style={{ height: containerHeight, overflow: 'auto' }}
      onScroll={handleScroll}
    >
      <Table>
        <TableHeader>
          <TableRow>
            <TableHeaderCell>ID</TableHeaderCell>
            <TableHeaderCell>Name</TableHeaderCell>
            <TableHeaderCell>Email</TableHeaderCell>
            <TableHeaderCell>Score</TableHeaderCell>
          </TableRow>
        </TableHeader>
        <TableBody 
          className="virtual-body"
          style={{
            height: data.length * itemHeight,
            paddingTop: startIndex * itemHeight
          }}
        >
          {visibleItems.map(item => (
            <TableRow key={item.id} style={{ height: itemHeight }}>
              <TableCell>{item.id}</TableCell>
              <TableCell>{item.name}</TableCell>
              <TableCell>{item.email}</TableCell>
              <TableCell>{item.score}</TableCell>
            </TableRow>
          ))}
        </TableBody>
      </Table>
    </div>
  );
}
```